<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hisa Araki</title>
    <description>The latest articles on DEV Community by Hisa Araki (@maskedridersystem).</description>
    <link>https://dev.to/maskedridersystem</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3870770%2F75af346f-4c2f-4ed0-a24c-a6609ae7a0d3.png</url>
      <title>DEV Community: Hisa Araki</title>
      <link>https://dev.to/maskedridersystem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maskedridersystem"/>
    <language>en</language>
    <item>
      <title>Rendering PDF/PNG reports in Node.js without Chromium or a subprocess (napi-rs)</title>
      <dc:creator>Hisa Araki</dc:creator>
      <pubDate>Sat, 17 Oct 2026 09:45:16 +0000</pubDate>
      <link>https://dev.to/maskedridersystem/rendering-pdfpng-reports-in-nodejs-without-chromium-or-a-subprocess-napi-rs-b44</link>
      <guid>https://dev.to/maskedridersystem/rendering-pdfpng-reports-in-nodejs-without-chromium-or-a-subprocess-napi-rs-b44</guid>
      <description>&lt;p&gt;Most Node.js apps that need to generate PDF reports — invoices, receipts, delivery&lt;br&gt;
slips — end up picking one of two approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch &lt;strong&gt;headless Chromium&lt;/strong&gt; (Puppeteer) and print an HTML page to PDF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shell out to a separate CLI tool&lt;/strong&gt; and parse its output.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both work. Both also come with overhead that has nothing to do with your actual&lt;br&gt;
report: Chromium's startup cost and memory footprint, or a child process's spawn&lt;br&gt;
latency and fragile stdout/stderr error handling.&lt;/p&gt;

&lt;p&gt;We just released &lt;a href="https://www.npmjs.com/package/@acrossreport/acr-node" rel="noopener noreferrer"&gt;&lt;code&gt;@acrossreport/acr-node&lt;/code&gt;&lt;/a&gt;,&lt;br&gt;
a native Node.js/Electron binding that renders PDF/PNG reports &lt;strong&gt;in-process&lt;/strong&gt;, with no&lt;br&gt;
Chromium and no subprocess involved.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it's built on
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://acrossreport.com" rel="noopener noreferrer"&gt;ACR (Across Report)&lt;/a&gt; is a JSON-template-based report&lt;br&gt;
rendering engine written in Rust on top of Google Skia. It already powered a CLI tool,&lt;br&gt;
a VS Code extension, and a desktop designer app — the same renderer, same pixel output,&lt;br&gt;
across all of them. &lt;code&gt;acr-node&lt;/code&gt; is a thin &lt;a href="https://napi.rs/" rel="noopener noreferrer"&gt;napi-rs&lt;/a&gt; binding that exposes&lt;br&gt;
that same Rust core directly to Node.js as a native addon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Template (JSON) → layout engine → Skia renderer → PDF / PNG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Same template produces byte-identical pixels on Windows/macOS/Linux (true WYSIWYG).&lt;/li&gt;
&lt;li&gt;Preview and print output match exactly — no Chromium print-to-PDF layout drift.&lt;/li&gt;
&lt;li&gt;A section-based layout model (PageHeader/GroupHeader/Detail/GroupFooter/PageFooter)
familiar to anyone who's used traditional report designers (Crystal Reports,
ActiveReports, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why napi-rs over a CLI wrapper
&lt;/h2&gt;

&lt;p&gt;We considered two designs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A — shell out to the existing CLI tool.&lt;/strong&gt; Simplest to build, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Process-spawn overhead adds up fast on a server generating many reports.&lt;/li&gt;
&lt;li&gt;Error handling over stdout/stderr is brittle.&lt;/li&gt;
&lt;li&gt;Binary placement and permissions differ per OS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;B — native addon via napi-rs.&lt;/strong&gt; Calls the existing Rust core (&lt;code&gt;acr_core&lt;/code&gt; /&lt;br&gt;
&lt;code&gt;acr_renderer_skia&lt;/code&gt;) directly through FFI, no process boundary, and napi-rs has solid&lt;br&gt;
prior art for both Node.js and Electron with a mature prebuilt-binary distribution&lt;br&gt;
story. Since the Rust engine already existed, there was no good reason to introduce a&lt;br&gt;
process boundary — we went with B.&lt;/p&gt;

&lt;p&gt;We'd already built a similar napi-rs binding for a VS Code extension&lt;br&gt;
(&lt;code&gt;acr_vscode_native&lt;/code&gt;), so this followed an established pattern rather than starting&lt;br&gt;
from zero.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation notes
&lt;/h2&gt;

&lt;p&gt;The binding itself doesn't touch the existing Rust rendering logic at all — it's a thin&lt;br&gt;
&lt;code&gt;#[napi]&lt;/code&gt;-annotated wrapper around functions that already existed. Two details worth&lt;br&gt;
calling out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Panic handling.&lt;/strong&gt; If Rust code panics across the napi-rs boundary, it takes the
whole Node.js process down with it. A malformed template JSON is a user error, not a
reason to crash the host app, so every exported function is wrapped in
&lt;code&gt;catch_unwind&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Licensing.&lt;/strong&gt; Unlicensed rendering adds a watermark to the output (same policy as
the CLI/Designer/Viewer tools). The activation cache file uses the same format across
all ACR products, so if a license is already activated on the machine by any of them,
&lt;code&gt;acr-node&lt;/code&gt; picks it up automatically — no duplicate activation needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Cross-platform builds
&lt;/h2&gt;

&lt;p&gt;napi-rs follows an &lt;code&gt;index.&amp;lt;platform-triple&amp;gt;.node&lt;/code&gt; naming convention and generates a&lt;br&gt;
loader (&lt;code&gt;index.js&lt;/code&gt;) that &lt;code&gt;require&lt;/code&gt;s the right binary for the current OS/arch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;win32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;nativeBinding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.win32-x64-msvc.node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arm64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;nativeBinding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.win32-arm64-msvc.node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;linux&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;// glibc/musl, x64/arm64 ...&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;darwin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;// x64/arm64 ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We built and bundled binaries for six platform/arch combinations on real hardware&lt;br&gt;
(Windows x64, Windows ARM64, Intel Mac, Apple Silicon Mac, Ubuntu x64, Ubuntu ARM64 on&lt;br&gt;
WSL2):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Windows x64&lt;/td&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows ARM64&lt;/td&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux x64 (glibc)&lt;/td&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux ARM64 (glibc)&lt;/td&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS (Apple Silicon)&lt;/td&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS (Intel)&lt;/td&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cross-building in CI isn't set up yet — this round was built machine-by-machine with&lt;br&gt;
&lt;code&gt;cargo build --release&lt;/code&gt;. Automating that is next on the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @acrossreport/acr-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;renderPdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;renderPreview&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;renderPngZip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;listDataFields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;licenseStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@acrossreport/acr-node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invoice.template.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invoice.data.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// PDF output (72dpi). Returns the page count.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dataJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invoice.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// PNG-per-page array for on-screen preview (96dpi)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dataJson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// pages[0].png is a Buffer&lt;/span&gt;

&lt;span class="c1"&gt;// Activate a license key (blocking HTTP request — call from a worker in Electron)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;you@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACR-XXXX-XXXX-XXXX-XXXX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same API whether you're calling it from a bare Node server (Express, Next.js API&lt;br&gt;
routes) or an Electron main process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PDF/PNG report rendering in Node.js/Electron, wired directly into a Rust/Skia engine
via napi-rs — no Chromium, no subprocess.&lt;/li&gt;
&lt;li&gt;The existing Rust rendering logic (&lt;code&gt;acr_core&lt;/code&gt; / &lt;code&gt;acr_renderer_skia&lt;/code&gt;) is untouched;
only a thin binding layer was added.&lt;/li&gt;
&lt;li&gt;Licensing is shared with the existing CLI/Designer/Viewer tools, so one activation
per machine covers all of them.&lt;/li&gt;
&lt;li&gt;Prebuilt binaries for six platforms ship in the package — no build toolchain required
on the consumer side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/acrossreport/acr-node" rel="noopener noreferrer"&gt;https://github.com/acrossreport/acr-node&lt;/a&gt;&lt;br&gt;
npm: &lt;a href="https://www.npmjs.com/package/@acrossreport/acr-node" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@acrossreport/acr-node&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the napi-rs setup, the panic-safety boundary, or the&lt;br&gt;
Skia rendering pipeline in the comments.&lt;/p&gt;

</description>
      <category>node</category>
      <category>rust</category>
      <category>electron</category>
      <category>webdev</category>
    </item>
    <item>
      <title>ACR is designed as a printer-independent report rendering service, not just a library.</title>
      <dc:creator>Hisa Araki</dc:creator>
      <pubDate>Sun, 12 Apr 2026 14:32:27 +0000</pubDate>
      <link>https://dev.to/maskedridersystem/acr-is-designed-as-a-printer-independent-report-rendering-service-not-just-a-library-11d3</link>
      <guid>https://dev.to/maskedridersystem/acr-is-designed-as-a-printer-independent-report-rendering-service-not-just-a-library-11d3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I have added an HTTP interface to the report engine ACR (Across Report Renderer).&lt;/p&gt;

&lt;p&gt;By making ACR available over HTTP, it can be used as a rendering service independent from the application.&lt;/p&gt;

&lt;p&gt;Previously, report systems had to be implemented separately depending on the development environment (Desktop, Web), OS, and runtime environment.&lt;br&gt;
With ACR, you can now generate PDF / PNG / HTML simply by sending a template and data.&lt;/p&gt;
&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;• Generate reports via HTTP
• Language-independent (HTTP-based)
• Can be called from Node.js / C# / Python / Java
• Output formats: PDF / PNG / HTML (PNG embedded in HTML)
• Test directly from a browser (Web UI included)
• Runs on Windows / Linux / macOS
• Can run as a service (daemon) on all supported OS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;⸻&lt;/p&gt;
&lt;h2&gt;
  
  
  Start
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./acr_http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  You can also specify port and template directory:
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./acr_http &lt;span class="nt"&gt;--port&lt;/span&gt; 9000 &lt;span class="nt"&gt;--templates&lt;/span&gt; /opt/acr/templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Web UI
&lt;/h2&gt;

&lt;p&gt;Open the following in your browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8765/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzj3fshdztga60zp9ijo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzj3fshdztga60zp9ijo.png" alt=" " width="800" height="825"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  curl（PDF）
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8765/render/pdf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "template": "invoice",
    "data": {
      "items": [
        { "name": "Apple", "price": 100 },
        { "name": "Orange", "price": 200 }
      ]
    }
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; out.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  curl（PNG）
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8765/render/png &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "template": "invoice", "data": { "items": [] } }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; out.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  curl (HTML)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8765/render/html &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "template": "invoice", "data": { "items": [] } }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running as a service
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Windows
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;acr_http.exe &lt;span class="nt"&gt;--install&lt;/span&gt;
sc start AcrHttpServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Linux (systemd)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;acr_http
systemctl start  acr_http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  macOS
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;launchctl load ~/Library/LaunchAgents/com.acrossreport.acr_http.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;• Rust-based engine
• Rendering with Skia
• Runs as an HTTP server
• Template and data are separated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;Summary&lt;/p&gt;

&lt;p&gt;The HTTP version is already released for evaluation.&lt;br&gt;
    • Command-line renderer: acr-cli&lt;br&gt;
    • Report designer: acr-designer&lt;br&gt;
    • RPX converter for ActiveReports: rpx2json&lt;br&gt;
⸻&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;p&gt;The following features are planned:&lt;br&gt;
• WebAssembly (WASM) support for browser execution&lt;br&gt;
• Receipt printer support (ESC/POS)&lt;br&gt;
• Support for printer command languages (ZPL / SBPL / TPCL)&lt;/p&gt;

&lt;p&gt;This will extend ACR beyond server-side usage to embedded devices, edge environments, and browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/acrossreport" rel="noopener noreferrer"&gt;https://github.com/acrossreport&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>csharp</category>
      <category>avalonia</category>
      <category>printer</category>
    </item>
    <item>
      <title>I built a pixel-perfect, printer-independent report designer with Avalonia UI</title>
      <dc:creator>Hisa Araki</dc:creator>
      <pubDate>Sun, 12 Apr 2026 06:09:25 +0000</pubDate>
      <link>https://dev.to/maskedridersystem/i-built-a-pixel-perfect-printer-independent-report-designer-with-avalonia-ui-4dke</link>
      <guid>https://dev.to/maskedridersystem/i-built-a-pixel-perfect-printer-independent-report-designer-with-avalonia-ui-4dke</guid>
      <description>&lt;p&gt;I’ve released ACR Designer, a WYSIWYG report designer for the ACR (Across Report Renderer) engine.&lt;/p&gt;

&lt;p&gt;It focuses on pixel-perfect layout and printer-independent rendering.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WYSIWYG report designer&lt;/li&gt;
&lt;li&gt;Pixel-perfect layout (dot-level accuracy)&lt;/li&gt;
&lt;li&gt;Printer-independent rendering&lt;/li&gt;
&lt;li&gt;Built with C# and Avalonia UI&lt;/li&gt;
&lt;li&gt;Runs natively on Windows and macOS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub:&lt;br&gt;
&lt;a href="https://github.com/acrossreport/acr-designer" rel="noopener noreferrer"&gt;https://github.com/acrossreport/acr-designer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is my first time building a full cross-platform desktop application with Avalonia, and I was impressed by how smoothly it works across platforms.&lt;/p&gt;

&lt;p&gt;Feedback is welcome.&lt;/p&gt;




&lt;h2&gt;
  
  
  Personal Note
&lt;/h2&gt;

&lt;p&gt;I’ve also released the macOS version of ACR Designer.&lt;/p&gt;

&lt;p&gt;At 66, this project has been an incredibly valuable learning experience for me.&lt;/p&gt;

&lt;p&gt;I truly appreciate all the support, and I’ll keep improving it whenever I can.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>rust</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built a pixel-perfect, printer-independent report designer with Avalonia UI</title>
      <dc:creator>Hisa Araki</dc:creator>
      <pubDate>Sun, 12 Apr 2026 04:17:00 +0000</pubDate>
      <link>https://dev.to/maskedridersystem/i-built-a-pixel-perfect-printer-independent-report-designer-with-avalonia-ui-5ddl</link>
      <guid>https://dev.to/maskedridersystem/i-built-a-pixel-perfect-printer-independent-report-designer-with-avalonia-ui-5ddl</guid>
      <description>&lt;p&gt;I’ve released ACR Designer, a WYSIWYG report designer for the ACR (Across Report Renderer) engine.&lt;/p&gt;

&lt;p&gt;It focuses on pixel-perfect layout and printer-independent rendering.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WYSIWYG report designer&lt;/li&gt;
&lt;li&gt;Pixel-perfect layout (dot-level accuracy)&lt;/li&gt;
&lt;li&gt;Printer-independent rendering&lt;/li&gt;
&lt;li&gt;Built with C# and Avalonia UI&lt;/li&gt;
&lt;li&gt;Runs natively on Windows and macOS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub:&lt;br&gt;
&lt;a href="https://github.com/acrossreport/acr-designer" rel="noopener noreferrer"&gt;https://github.com/acrossreport/acr-designer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is my first time building a full cross-platform desktop application with Avalonia, and I was impressed by how smoothly it works across platforms.&lt;/p&gt;

&lt;p&gt;Feedback is welcome.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>showdev</category>
      <category>ui</category>
    </item>
    <item>
      <title>Why I built a pixel-perfect report engine with Rust and Skia</title>
      <dc:creator>Hisa Araki</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:23:19 +0000</pubDate>
      <link>https://dev.to/maskedridersystem/why-i-built-a-pixel-perfect-report-engine-with-rust-and-skia-4g76</link>
      <guid>https://dev.to/maskedridersystem/why-i-built-a-pixel-perfect-report-engine-with-rust-and-skia-4g76</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For many years, I have been working on business systems where report generation is a critical component.&lt;/p&gt;

&lt;p&gt;However, I repeatedly encountered the same problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout differences depending on printer drivers&lt;/li&gt;
&lt;li&gt;OS-specific rendering inconsistencies&lt;/li&gt;
&lt;li&gt;Difficulty achieving true WYSIWYG output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These issues are not new, but they are still not fully solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In traditional report systems, rendering often depends on external components such as printer drivers or OS-level graphics APIs.&lt;/p&gt;

&lt;p&gt;This leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slight layout shifts&lt;/li&gt;
&lt;li&gt;Inconsistent text measurement&lt;/li&gt;
&lt;li&gt;Different output between environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For business reports, even small differences can be unacceptable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;p&gt;To solve this, I designed a report engine with the following principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully deterministic layout&lt;/li&gt;
&lt;li&gt;Pixel-perfect rendering&lt;/li&gt;
&lt;li&gt;No dependency on printer drivers&lt;/li&gt;
&lt;li&gt;Cross-platform consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engine directly controls rendering using Skia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust
&lt;/h2&gt;

&lt;p&gt;I chose Rust for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory safety&lt;/li&gt;
&lt;li&gt;Predictable performance&lt;/li&gt;
&lt;li&gt;Strong control over low-level behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is important for a rendering engine where consistency is critical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Highlights
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Layout engine with pre-calculated positioning&lt;/li&gt;
&lt;li&gt;Pagination control&lt;/li&gt;
&lt;li&gt;Consistent font measurement&lt;/li&gt;
&lt;li&gt;Rendering abstraction over Skia&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The engine produces identical output (PDF / PNG) across environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/acrossreport" rel="noopener noreferrer"&gt;https://github.com/acrossreport&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;If you are interested in rendering engines, Skia, or cross-platform layout systems, I would appreciate your feedback.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>skia</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
