← dompdf.js Studio

dompdf.js vs Canvas-Based Rendering: The Engineering Choice

Frontend PDF generation splits into two fundamentally different technical families. The first, represented by libraries like jsPDF combined with html2canvas, paints the page onto a Canvas element and then writes that bitmap data into the PDF. The second, represented by dompdf.js, parses the DOM and its computed CSS directly and emits a vector PDF from a Rust/WASM core. The two families diverge at the level of rendering philosophy, and their outputs differ just as deeply. A Canvas-generated PDF is, in essence, a picture: text cannot be selected, copied, or searched, zooming past one hundred percent reveals soft edges, and file sizes balloon because every page is a compressed image. A dompdf.js PDF stores every character as a real text object with embedded font outlines, so text is selectable and searchable, zoom is crisp at any magnification, and a thousand-page document renders in about two seconds. This article compares the two approaches across six dimensions — rendering principles, text capabilities, file size, performance, memory, and maintainability — then gives concrete guidance for choosing between them and a step-by-step migration path for teams currently on a Canvas-based pipeline. The goal is to help you make the decision at project inception, when the choice is cheap, rather than after thousands of documents have been generated with the wrong tool. No single benchmark can settle the question for every project, so the article ends with a practical evaluation method: run your own templates through both pipelines, measure time, size, and text usability, and decide on evidence rather than vendor claims.

Two Rendering Philosophies: Raster vs Vector

The typical Canvas pipeline works like this: html2canvas walks the page and produces a bitmap screenshot of the target area, and jsPDF takes that bitmap and places it into a PDF page. No real text information survives the journey — only pixels. Each PDF page is effectively one large image, which is why text in such documents cannot be selected or searched. To keep file sizes reasonable, the bitmap is usually JPEG-compressed, and compression trades directly against legibility: the higher the compression ratio, the blurrier the text edges. Sharpness and file size are locked in an inherent conflict that no parameter tuning fully resolves.

dompdf.js follows a different path entirely. It reads the DOM tree and the computed styles of every node, then converts each text node and each box model into vector instructions in the PDF. Text is written as font outlines, images are embedded as image objects, and backgrounds and borders become vector graphics. The PDF is a structured collection of objects with a separate text layer and graphics layer, so readers can parse the content precisely. This is a capability that no bitmap-based approach can replicate, because the information simply was never captured in the first place.

The philosophical difference dictates the ceiling of what each approach can do. Vector output enables text search, copy-and-paste, screen-reader support, and even downstream re-editing; raster output can only offer something that looks like a document. For contracts, invoices, reports, and legal documents — anything that must be archived, indexed, or printed — vector rendering is a baseline requirement rather than a nice-to-have. Teams that treat it as an afterthought usually discover the hard way that the product requirement was really a rendering-architecture requirement in disguise.

Text Selectability and Full-Text Search

In a raster PDF, text is pixels. When a user tries to select a line of text, they drag a box over a picture; copying yields garbage characters, and searching the document finds nothing. In archives, legal workflows, and knowledge-base systems this is a fatal defect: documents must be searchable to be useful, and the only workaround for bitmap output is running OCR on top, which costs money, adds latency, and makes errors on long-tail characters that quietly corrupt the index.

dompdf.js preserves the full text layer with character and position information, so browsers and PDF readers can select, copy, and search the content directly. Mixed Chinese, English, and numeric text works the same way, and font embedding guarantees identical glyphs on any device that opens the file. Systems that need full-text indexing can extract text straight from the exported PDF with no OCR in the loop, which keeps index quality high and construction costs low. The text layer is not a feature you configure; it is an intrinsic property of how the file is written.

Printing exposes a subtler but equally real difference. When a raster PDF is printed, the printer driver re-rasterizes embedded images, and color fidelity and edge sharpness typically degrade another notch. A vector PDF is printed by rendering the outlines directly, so text stays crisp and colors stay accurate. The same file, delivered through a different medium, shows the raster disadvantage amplified. For organizations that print contracts or mail reports to customers, this is the difference users notice immediately and complain about politely.

There is one more consideration that rarely appears in vendor comparisons: accessibility. Screen readers can traverse the text layer of a vector PDF, making documents usable by assistive technology. Bitmap PDFs are opaque to screen readers unless OCR text is layered on top. For public-sector and enterprise deployments where accessibility compliance is mandated, vector output is not a preference but a requirement, and retrofitting it later is far more expensive than choosing it up front.

Code Example: Capability Comparison Table

The table summarizes the differences across six dimensions — rendering principle, text capability, zoom quality, file size, performance, and font handling — in a form you can paste directly into a technical review document. The comparison uses the typical implementations as reference points: html2canvas combined with jsPDF on the raster side, and the dompdf.js WASM pipeline on the vector side. Exact numbers vary with versions, but the direction of every row is stable and will not flip because of a minor release.

The two-second figure for a thousand pages comes from the dompdf.js official baseline for typical long documents on mainstream desktop browsers. The raster side has no comparable single number, because per-page screenshot time scales with page complexity and long documents are measured in minutes, not seconds. Before making a final decision, run your own real templates through both pipelines and record three numbers: wall-clock time, output size, and whether the text is selectable. Measured evidence beats any marketing claim, and it takes an afternoon to gather.

One nuance worth noting: the raster approach is not uniformly bad at everything. If the source page is dominated by images and visual effects, the screenshot may actually look closer to what the user saw in the browser than a vector re-rendering of loosely specified CSS. The comparison table is about the document's usable properties — text, size, performance — not about raw visual fidelity in every edge case. Choose the dimension that matters for your deliverable, not the one that flatters a demo.

| Dimension | Canvas approach (html2canvas + jsPDF) | dompdf.js (WASM vector pipeline) |
| --- | --- | --- |
| Rendering principle | DOM screenshot to bitmap, embedded in PDF | DOM/CSS parsed into vector instructions |
| Selectable / searchable text | No; copying yields garbage | Yes; select, copy, full-text search |
| Zoom sharpness | Blurry edges beyond 100% | Crisp at any magnification (font outlines) |
| File size | Large (a JPEG/PNG per page) | Small (text + vector instructions) |
| Long-document performance | Per-page screenshots, minutes | ~2 seconds for 1000 pages (official baseline) |
| Font embedding | Constrained by bitmap capture | Full TTF/OTF embedding, consistent glyphs |
| Dependencies | Two libraries to integrate and version | One library, ESM/CJS dual format |
| Best fit | Simple previews, visual snapshots | Contracts, reports, invoices, formal docs |

File Size and Rendering Performance

File size differences follow directly from the information format. A bitmap encodes a page of content as tens of thousands to millions of pixels, and JPEG compression is notably unfriendly to text edges; a single A4 page screenshot typically lands somewhere between a few hundred kilobytes and several megabytes. A vector PDF stores text as outline instructions, so a page of plain text often comes in at tens of kilobytes. The same content can differ by an order of magnitude in size, which is a real cost in email attachments, object storage, and bandwidth — and the gap widens further in batch-export scenarios.

On performance, the raster pipeline's bottleneck is per-page screenshotting and encoding: the more complex the page, the slower the capture, and long documents are effectively minute-scale operations, with the page frozen during the wait. dompdf.js runs typesetting and encoding inside native code, so a thousand-page document renders in roughly two seconds, and because rendering happens in a worker, the page keeps responding throughout. The user experience is not a slightly faster version of the old one; it is a different category of interaction — background progress instead of a frozen tab.

Latency also affects architecture. With minute-scale exports, you are pushed toward server-side generation or asynchronous job systems just to make the feature tolerable; with second-scale exports, client-side generation becomes practical, which removes server load, removes queue infrastructure, and lets the user see the result immediately. The performance difference is not merely cosmetic — it changes where the export pipeline can live and how much infrastructure it needs, which is often the deciding factor for small teams.

For a quick internal benchmark, generate the same five-page document with both pipelines and compare the numbers in the table's rows — time, size, and text selectability. Five pages is enough to expose the differences, and the results usually make the architectural choice obvious.

Memory Footprint and Long-Document Behavior

Memory behavior separates the two approaches even when a single page looks fine. The raster pipeline must hold the DOM screenshot and the PDF data simultaneously, and with multiple large images resident in memory, long documents can push browsers toward memory pressure or outright crashes. Batch exports multiply the problem: each queued document adds another set of bitmaps, and memory grows roughly linearly with the batch size. Teams frequently discover this only after production incidents.

The vector pipeline works with text and graphic instructions, which are compact by nature. Even a multi-hundred-page document keeps a modest memory profile, because page content is stored as structured objects rather than pixel buffers. Batch exports stay stable for much longer, and the memory curve stays flat enough to monitor with confidence. For systems that generate many documents per day, this difference translates directly into fewer crashes, fewer support tickets, and less operational firefighting.

There is also a practical testing angle. Raster pipelines are hard to test deterministically: screenshot output varies with the browser, the machine, and even the scroll position, so regression testing relies on pixel diffing with generous thresholds. Vector output is deterministic given the same HTML and fonts, which means you can assert on extracted text, page counts, and precise layout coordinates. Tests that were flaky become reliable, and teams can finally automate the export quality checks they previously performed by hand.

Network delivery amplifies the size gap in practice. Emails with attached reports, file shares, and document management systems all pay storage and transfer costs per byte, so an order-of-magnitude size reduction compounds across every copy of every document the system produces. For organizations generating thousands of PDFs a month, the savings are not theoretical.

Choosing the Right Approach for Your Scenario

When does the Canvas approach remain a legitimate choice? If the requirement is simply to snapshot a page and send the picture to someone — no text search, no zooming into details, and the content is dominated by images and visual styling — the raster pipeline is simpler to integrate and looks faithful to the original. Share cards, visual previews, and throwaway exports fall into this category. Choosing vector rendering for these would add integration cost without delivering user-visible value.

When is vector rendering mandatory? Contracts, invoices, statements, legal documents, technical documentation — anything that must be archived, searched, printed, or copied. Add to that list any system with high export volume, where size and speed advantages translate directly into storage savings and user experience. If a document will outlive the session in which it was generated, it deserves a text layer. This single question — will this document need to be searched or reused later? — resolves most selection debates faster than any feature matrix.

Hybrid deployments are perfectly reasonable. Keep a bitmap snapshot for the preview thumbnail, and route the formal export through the vector pipeline. The two paths coexist without conflict; you just branch the export entry point by purpose. The critical discipline is to clarify the requirements up front: ask whether documents must be searchable, printable, and archived before choosing the architecture. Most teams that regret their choice did not fail at technology selection; they failed to ask the right product question early enough.

Long-document behavior also shapes user workflows: when exports finish in seconds, users generate documents interactively and iterate on the result; when they take minutes, users batch the work and walk away, which hides mistakes until much later. The rendering approach thus influences not just performance but how the product is used day to day.

Migrating from Canvas-Based Export

The first migration step is template inventory. Extract the HTML used for export from business code so the export template is decoupled from the on-screen page: no runtime state dependencies, stable image URLs or data URLs, and explicit font declarations. Once the template is independent, switching the export function's internals to dompdf.js is a localized change — callers barely notice — and the risk surface is confined to the template layer rather than the whole application.

Run both pipelines in parallel during the transition. The same data flows through the old and new export paths, and an automated comparison script spot-checks text completeness and layout on key pages. Only when the new output demonstrably matches or beats the old one on text, pagination, and fonts do you flip the default entry point and retire the old code. Every step is reversible, so the team can validate the new pipeline against real traffic without committing to it prematurely.

The payoff after migration compounds. File sizes drop by an order of magnitude, export time falls from minutes to seconds, text becomes searchable, and print quality improves. The engineering time saved on waiting, storage, and support tickets can be reinvested in export features that were previously impractical: custom templates, batch archiving, export history. Teams that make the switch typically describe it as paying off a technical debt that then starts paying interest in the other direction — every subsequent feature builds on a foundation that is faster, smaller, and more capable.

Finally, plan the migration as a project with an owner and an exit criterion, not as a background task. Define the acceptance checklist — build warnings, production export success, text-layer verification, pagination parity — and hold the old pipeline in place until every item passes. The dual-run period is the cheapest insurance you will ever buy against export regressions, and it makes the cutover a non-event instead of a high-risk release.

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。