Tip
Want the app, not the SDK? DartPDF is the official PDF editor built on this library. Edit, annotate, sign, and fill PDFs entirely on your device, with no account and no uploads. Get it at dart-pdf.com or open it in your browser.
Need to complete a form? Fill and sign a PDF with DartPDF explains existing fields, flat forms, drawn signatures, and saving a checked copy.
An open-source Flutter PDF editor, viewer, and renderer written entirely in Dart. No PDFium, no platform channels.
The goal is a PSPDFKit-class SDK built natively for Flutter: a fast viewer, a full annotation suite with appearance-stream generation, AcroForm filling, page manipulation, and editing that preserves digital signatures.
Feature parity is only the foundation. The UX vision defines the product north star, the journeys that matter, how their quality is measured, and the definition of done for user-facing work.
Status: the roadmap below is complete. COS parsing with xref recovery, signature-preserving incremental updates with encrypt-on-write, a content-stream interpreter with TrueType/CFF/Type3 font rendering, mesh shadings, ICC color, pure-Dart CCITT/JBIG2/JPEG 2000 decoders, a zoomable Flutter viewer with text selection and search, inferred text reflow, annotation authoring and flattening, AcroForm filling, page manipulation, digital signatures with trust-store chain validation, content editing, and the full editing UI: tools, panels, forms, touch/stylus input, theming, and a sync surface for collaborative annotation stores.
Live demo: https://dart-pdf-demo.web.app (the example app built for the web; it opens onto a six-page feature showcase, and the open button loads your own PDF).
Developer overview: Flutter PDF editor
Step-by-step integration: How to add PDF editing to a Flutter app with installation, architecture, supported editing features, and benchmarks.
Visual render results, browsable directly in GitHub: the checked-in PDF.js corpus comparison gallery shows PDF.js baselines, Dart renders, and diffs side by side, and the Ghent Output Suite render gallery shows every print-conformance page we rasterize (our own renders, which are the baselines - so no diff column).
import 'package:pdf_document/pdf_document.dart';
final mergedBytes = PdfMerger.merge([firstPdfBytes, secondPdfBytes]);The bytes-only API runs on desktop, mobile, and web. It preserves the first PDF's encryption and viewing settings, imports form fields and bookmarks, and remaps named links. See merge options and collision rules.
In DartPDF, choose Insert document… from the app menu to insert a PDF after the current page. Dropping PDFs on the thumbnail strip inserts at the marked position; dropping on the document offers Insert pages to append them. Each inserted file is one undo step and uses the existing save/recovery session.
In DartPDF, choose Reduce file size… from the app menu. Start with Lossless, or choose Screen, eBook, or Print to trade image resolution for a smaller file. Advanced settings control individual passes, target DPI, and JPEG quality. Review the before/after sizes and category breakdown, then Save copy using the platform's save/download/share flow.
The library exposes the same optimiser through PdfCompressor.optimize and
PdfEditor.compress. See compression options and supported content.
The default viewer has not reached PDFium interaction parity yet. The most
recent real-document checkpoint (2 October 2026, commit fadf7760) used five
interleaved DartPDF/PDFium runs in Chrome 154 on an M1 Pro, a 1400×1000
viewport, and the default JS/CanvasKit web build. The input was a locally
supplied 62-page, 24.1 MB illustrated PDF; the journey opened it, jumped to
pages 3 and 47, zoomed to 1.72×, and drove matched wheel gestures. Lower
ratios are better.
| user-visible metric | DartPDF p50 / p95 | PDFium p50 / p95 | ratio p50 / p95 |
|---|---|---|---|
| open to stable visual | 711 / 757 ms | 1264 / 1411 ms | 0.56× / 0.54× |
| page first visual change | 24 / 31 ms | 19 / 22 ms | 1.26× / 1.39× |
| page stable visual | 271 / 357 ms | 118 / 121 ms | 2.30× / 2.95× |
| zoom stable visual | 44 / 47 ms | 19 / 25 ms | 2.32× / 1.92× |
| wheel journey | 413 / 919 ms | 972 / 1022 ms | 0.42× / 0.90× |
| wheel rAF interval p95 | 83 ms | 17 ms | 4.96× |
| peak browser RSS p50 | 2167 MiB | 1887 MiB | 1.15× |
The wheel journey completes sooner, but its worse rAF tail means it is not yet as smooth; total duration alone would be a misleading win. Headless Chrome 154 appears to pace frames at 60 Hz on this host (PDFium's rAF p95 is 17 ms, where it was 10 ms in August), so the cadence ratio is not directly comparable with earlier checkpoints. Open and memory are inside the current provisional budgets (memory with less headroom than in August), while stable navigation, zoom, and scroll cadence are not. These numbers describe desktop web only and are not a native-desktop or mobile parity claim. See the full methodology and historical experimental results in the PDFium parity notes.
The offline corpus benchmark remains useful as a subsystem diagnostic, not as
evidence of viewer latency: over the 52-file / 268-page common subset at scale
2, pure-Dart interpretation takes 10.3 ms/page, PDFium rasterization takes
31.1 ms/page, and the complete Flutter raster plus readback takes 54.0 ms/page.
Reproducible offline harnesses and file-by-file diffs live in
benchmark/.
Strictly layered packages; Flutter and dart:ui stay in the editor and its
optional Flutter companions, so the core runs on servers and in plain Dart tests. Published packages use their
directory name on pub.dev.
| Package | pub.dev | Role |
|---|---|---|
pdf_cos |
The PDF file format itself: tokenizer, parser, filters (incl. CCITT/JBIG2/JPX), encryption, cross-reference machinery, serializer, crypto primitives. | |
pdf_document |
Document semantics: page tree, annotations, AcroForm, digital signatures, and the incremental-save PdfEditor. |
|
pdf_graphics |
Content-stream interpreter, device interface, font engine, ICC color, text extraction. | |
dart_pdf_cli |
— | Pure-Dart dartpdf command line and stdio MCP server for bounded document inspection, text extraction, form listing, and annotation listing. |
dart_pdf_editor |
Flutter viewer and editing UI: canvas device, PdfViewer, tools, panels, forms. |
|
dart_pdf_printing |
Initial release pending | Optional print preview, sheet layout and native system printing on all six Flutter platforms. |
dart_pdf_editor_flutter_gpu |
Experimental opt-in Impeller/flutter_gpu tile renderer for supported native targets, with exact Canvas fallback for unsupported pages. |
|
dart_pdf_editor_assets |
Optional bundled editor fonts + web render worker (~4.2 MB package download); depend on it and call registerBundledEditorAssets() for the full editor, omit for a size-minimal viewer. |
|
pdf_ocr_ondevice |
Optional on-device OCR engine for native Flutter apps; downloads a small PP-OCR model once and adds searchable text layers offline. | |
pdf_ocr_vlm |
Optional HTTP OCR engine for web, mobile, and desktop; talks to dots.ocr/vLLM or any service returning text boxes. | |
pdf_test_fixtures |
Programmatic, structurally-correct PDF builders for tests. |
For shell, CI, and local agent workflows, run the pure-Dart CLI directly from the workspace:
dart run packages/dart_pdf_cli/bin/dartpdf.dart inspect document.pdf --json
dart run packages/dart_pdf_cli/bin/dartpdf.dart text document.pdf --pages 1-5 --jsonThe same handlers are exposed as bounded, read-only MCP tools through
dartpdf mcp. See the dart_pdf_cli guide for
installation (including the copy bundled in desktop app builds), filesystem
roots, password handling, and host registration.
For a Flutter app, add the editor package:
flutter pub add dart_pdf_editorThen give the drop-in shell PDF bytes and bounded space:
import 'package:dart_pdf_editor/dart_pdf_editor.dart';
PdfEditorView(
bytes: pdfBytes,
onSave: (bytes) => saveBytesSomewhere(bytes),
)
PdfReader(bytes: pdfBytes)To open a large PDF hosted remotely without downloading the whole file first,
use the asynchronous byte-source API. PdfHttpByteSource fetches over HTTP
Range requests (forwarding auth headers, with cancellation and progress), and
falls back to a plain download when the server does not support ranges:
final source = PdfHttpByteSource(
Uri.parse('https://host/big.pdf'),
headers: {'Authorization': 'Bearer $token'},
cancelToken: cancelToken,
onProgress: (received, total) => report(received, total),
);
final document = await PdfDocument.openSource(source);For an even faster first paint on a big, image-heavy document (a scan, a CAD
sheet), pass firstPaintPages so the loader fetches only the first page's
bytes, paints it, and lets you stream the rest in behind it:
final preview = await PdfDocument.openSource(
source,
options: PdfSourceLoadOptions(
firstPaintPages: 1,
onProgress: (received, total) => report(received, total),
),
);
// `preview` renders the first page immediately, but its buffer is deliberately
// incomplete - render-only, never edit or sign from it. Read the whole source
// in the background and reopen for the full document when you need every page.PdfByteSource is network-agnostic (length + readRange), so any random-
access transport works - including a local file. PdfDocument.open(Uint8List)
and the byte-based widgets are unchanged. See
doc/progressive-loading.md for the full guide
(local files, the fast-first-paint pattern, Flutter viewer wiring) and
doc/dev-log for the loader design.
For OCR, add one engine package and call PdfEditor.applyOcr before opening
or replacing the document in your viewer:
flutter pub add pdf_ocr_ondevice # native offline OCR
# or
flutter pub add pdf_ocr_vlm # HTTP OCR, including Flutter webpdf_ocr_ondevice is the simplest native path: it downloads the default model
once, caches it in app support storage, then runs offline. pdf_ocr_vlm is the
simplest web/server path: point it at a CORS-enabled OCR service. Both write the
same invisible text layer, so scanned pages become selectable, searchable, and
copyable without changing how the PDF looks.
pdf_document can generate multiple standalone PDFs directly from bytes:
final parts = PdfSplitter.splitExpression(bytes, '1-3, 7, 10-12');
// Three outputs: pages 1–3, page 7, and pages 10–12.
final firstThree = PdfSplitter.splitRange(bytes, 0, 2); // zero-based, inclusivePdfEditorView(onSplitPages: ...) adds a validated Split PDF… dialog to the
thumbnail page-actions menu. The example opens each result in a new editable tab.
Existing single-range and thumbnail-selection exports remain available.
Page annotations/resources and document information are retained; bookmarks, the document-level AcroForm field list, and named destinations are omitted. Links between pages retained in one output are remapped. Copied widgets are not registered in an AcroForm. Resources remain shared within an output where possible; each output owns its copies. Encrypted sources yield unencrypted outputs. See the splitting guide for indexing, password handling, validation, and extraction semantics.
- ✅ COS reader: lexer, parser, FlateDecode + predictors, xref tables, xref streams, object streams
- ✅ Incremental-update writer (signature-preserving); first edits: metadata, page rotation
- ✅ Encryption: opening RC4/AES-128/AES-256 documents (user and owner passwords) and encrypt-on-write, so encrypted documents are editable and signable (the signature's /Contents stays plain per ISO 32000 §7.6.1; every other string in the signed revision is encrypted).
- ✅ Content-stream interpreter with an abstract device interface
- ✅ Font engine: TrueType/CFF glyph outlines, CID fonts, CMaps, ToUnicode
- ✅ Flutter rendering device + viewer widget
- ✅ Text extraction with positions, feeding selection and search plus paragraph/reading-order inference for text reflow
- ✅ Annotations: model, appearance-stream rendering, authoring with generated appearances (highlight/underline/strike-out/squiggly, ink, shapes, free text, callouts, notes, stamps, measurements, count, and redaction), and flattening
- ✅ AcroForm: field model (text, check box, radio, choice), filling with regenerated appearances (auto-size, multiline wrap, quadding, /MK decorations), and template editing: field metadata, creating fields on any document, renaming, deleting, retyping, image-filled push buttons, and fault-tolerant whole-form flattening
- ✅ Page manipulation: reorder/move, remove, merge with cross-document
object copying (
appendPagesFrom), and split (extractPageswrites a standalone file; extracting from an encrypted document decrypts) - ✅ Digital signatures: validation (CMS/PKCS#7 with RSA and ECDSA
P-256/384/521 and RSASSA-PSS, byte-range and revision-coverage checks)
and signing (
saveSigned, adbe.pkcs7.detached, RSA-SHA256, verified interoperable with OpenSSL and poppler) - ✅ Content editing: stamping (
stampPage, text, shapes, JPEG and PNG images over existing content), element deletion (PdfPageElements+deleteElements), and text editing (replaceText, across a line's shown strings with width-compensated re-measurement;reflowTextrewraps supported single-column paragraphs, including compatible embedded Type0 fonts)
Deliberately deferred: JBIG2 Huffman/refinement variants and JPX subsampling/PCRL-CPRL progressions.
- Zooming/panning viewer with fit-page and fit-width modes, and deep-zoom detail rendering past the raster caps.
- Text selection with mouse or touch (long-press, then drag handles), full-text search, link navigation, outlines.
- Reader reflow mode infers visual lines, columns, reading order, and paragraphs from positioned text, then presents a continuous selectable text view for narrow screens.
- Custom always-visible scrollbars that stay usable while zoomed; a horizontal bar appears for panning the zoom window. Scroll metrics are computed from real page heights, so the thumb doesn't jump around on long documents with mixed page sizes.
- Trackpad pinch and scroll don't fight each other (each gesture commits to one or the other), touch pinch zoom works with a tool armed, and flings keep their momentum, including finger scrolls while an editing tool owns the gestures.
- Navigation (search results, links, thumbnails) accounts for the zoom window, so jumps land where the user is looking.
- During fast flings, pages show low-resolution cached previews rather than blank paper; full rendering resumes when scrolling settles, keeping heavy CAD documents smooth.
- Rendering is progressive: the default worker records pages off the UI thread, streams partial page records as they are produced, and reveals complex pages top-down instead of waiting for the whole page.
- The renderer supports print-oriented overprint and spot-color behavior, including image colorants, rather than approximating every overprint draw with a blend mode.
- Dark mode, arbitrary page background colors (
PdfViewer.pageColor), chrome theming viaPdfViewerTheme, and a hide-all-annotations toggle.
PdfEditingControllersaves incrementally on every edit, so revisions are byte prefixes of one buffer and undo/redo is essentially free.- Tools: text markup (highlight/underline/strikeout/squiggly), ink, freehand highlighting, shapes (including polylines, polygons, and revision clouds), free text, Bluebeam-style callouts, notes, stamps (including saved custom stamps), hyperlinks (URI and in-document), placed images with interactive cropping, and a saved ink signature.
- Callouts pair an editable text box with a leader line and arrowhead. The terminus and its attachment to the box can be repositioned independently, while moving the body keeps the whole callout together.
- A stamp on the page goes back into the stamp collection from its
right-click menu, so a design that arrived in a document is reusable.
Stamps this editor placed come back exactly - the vector design travels
with them,
{{date}}-style fields still live. - Takeoff tools calibrate a drawing's scale, then measure distance, perimeter, area, slope, angle, arc length, or volume. A count tool places and totals check-mark stamps.
- Selection works like a desktop editor: click, rubber-band marquee, shift/⌘-click toggle, ⌘A; move, resize, and rotate with live appearance previews and zoom-invariant chrome that follows rotated annotations without shearing.
- Shapes and text boxes regenerate their appearance on resize, keeping stroke width and font size constant (text re-wraps, in the preview too).
- The eraser slices ink: it removes exactly the stroke segments under the swept circle, splitting strokes where it crosses them.
- Copy/cut/paste via keyboard and context menu; copies are deep snapshots that survive undo, and the clipboard is shared across tabs, so an annotation copied in one open document pastes into another (even after the tab it came from is closed). Z-order edits reorder the page's /Annots array so they stick in any viewer. Touch reaches the same context menu with a long-press (on an annotation, a form field, or empty page area when the clipboard has something to paste).
- In-place restyling: color, stroke width, opacity, font, and size of the selected annotations, preserving identity, z-order, and authors.
- Lock and unlock annotations with interoperable PDF flags; customize keyboard shortcuts for every editing tool.
- Commits never flash: the overlay keeps the committed preview painted until the page re-render reaches the screen.
- Text boxes edit in place with an inline editor matching the committed font, size, color, and fill; Helvetica/Times/Courier with proper AFM metrics.
- The content tool can select, delete, rewrite, replace, or export page text, paths, and images. It also supports device-color replacement, irreversible redaction, and vector snapshots that can be pasted back without rasterizing.
- Stylus support: pressure-variable ink width, Catmull-Rom stroke smoothing, palm rejection, instant stroke start, dot taps, and inverted-pencil erasing. Ink auto-commits about a second after the pen lifts.
- Fill text, checkbox, radio, and choice fields in place; push-button fields take an image (the usual carrier for signatures and logos).
- Fields are highlighted with a translucent wash and border by default
(
PdfViewer.highlightFormFields), so they're findable at a glance. - Field administration (add, rename, retype, delete, flatten) is available both as an API and in the UI via drag-out creation and a right-click menu.
PdfEditor.injectTextLayerwrites already-recognizedPdfOcrSpans as an invisible PDF text layer. The original scan remains visually unchanged, but selection, search, copy, and text extraction start working.PdfEditor.applyOcr(pageIndex, engine)renders the page, calls a pluggablePdfOcrEngine, maps raster boxes back through crop boxes and/Rotate, and injects the layer for you.pdf_ocr_ondevicesupplies a native offline engine backed by ONNX Runtime and a downloadable PP-OCR model.pdf_ocr_vlmsupplies an HTTP engine for dots.ocr/vLLM, cloud VLMs, or a small adapter around Tesseract/PaddleOCR. Hosts can also implementPdfOcrEnginedirectly.
- Thumbnail sidebar: tap to jump, drag to reorder, delete, with a live viewport indicator. Tiles render into an LRU cache keyed by per-page render stamps, so an edit only re-renders the pages it touched.
- Annotation sidebar with live filtering and multi-select delete; tapping a tile zooms to the annotation and pulses an attention ring.
- Outline and bookmark sidebars let readers navigate the document and editors create, rename, and remove bookmarks.
- Properties panel: type, page, color, fill, stroke width, corner radius, opacity, font, contents, author, and numeric position/size, all editable.
- Search results panel with context snippets from page text and annotation
contents, plus
PdfSearchFieldand an editablePdfPageNumberField("3 / 12") for app bars. - All panels resize by dragging their inner edge. Panel widths, colors,
stroke width, fonts, theme mode, and the rest of the UI preferences
persist on the device (
PdfEditingPreferences, backed byshared_preferences). - A full color picker (
PdfColorPicker) with hex/RGB/HSL/CMYK entry and an eyedropper that samples from the rendered page.
- Every authored annotation carries a generated /NM name (the spec's own identifier), giving it durable identity across moves, restyles, and rewrites.
PdfAnnotationSnapshotserializes to JSON with the appearance stream included, so a synced annotation renders identically on every device.annotationChangesemits per-revision diffs (created/modified/removed, undo and redo included);applyRemoteChangereplays remote edits without echoing them back. Each remote apply is an undo checkpoint: local edits after it remain undoable, but undo cannot remove remote state or cross into older local history. Two controllers piped together converge.- Permissions: the document's /F ReadOnly and Locked flags are honored,
and a
canEditAnnotationpredicate covers policies like "users may only edit their own annotations". Gated annotations still render and list; they just can't enter the selection.
This repo uses fvm (Flutter 3.47.8) and pub workspaces.
fvm flutter pub get # resolve the whole workspace
fvm dart analyze
cd packages/pdf_cos && fvm dart testThe example app (packages/dart_pdf_editor/example) runs on macOS, iOS,
Android, web, Windows, and Linux, with platform-native file handling:
the system picker to open, and a save dialog, browser download, or share
sheet to save, depending on the platform.
It opens onto a generated six-page feature showcase: PDF link/JavaScript actions driving the Flutter app and live widgets pinned onto the page, a vector-graphics page (dashes, joins, Bézier fills, stitched axial and radial shadings, blend modes, constant alpha, CMYK/gray swatches), a typography page (the standard fonts, rendering modes, spacing/scaling operators, text transforms), an images page (RGB XObjects, color-key masks, 1-bit stencils, inline images), and an annotations & forms page whose markup, shapes, stamp, note, and filled form fields are authored through the editor API while the document is generated, so the demo doubles as a smoke test of the authoring pipeline.
test_corpora/ghent/ carries the Ghent PDF Output Suite
V5.0, 54 print-conformance PDFs covering overprint,
DeviceN/spot color, ICC v2/v4, 16-bit images, transparency blend modes,
softmasks, optional content, font formats, and JBIG2/JPEG 2000
compression. Two layers run over it:
pdf_graphics/test/ghent_corpus_test.dartinterprets every page on the plain Dart VM (parse + paint-op assertions, no rasterization).dart_pdf_editor/test/ghent_render_test.dartrasterizes every page and compares it pixel-wise against checked-in baseline renders; regressions dump actual/diff images for inspection, andGHENT_UPDATE=1re-baselines after an intentional change.
Those baselines are browsable as the
Ghent Output Suite render gallery.
Note what it is: with no external reference renderer for this corpus, our
renders are the baselines, so the gallery has no diff or pass/fail column
and a green ghent_render_test means "unchanged", not "conformant". Several
patches print their own pass criterion on the page. Regenerate the gallery
with fvm dart packages/dart_pdf_editor/tool/rebuild_ghent_render_index.dart.
test_corpora/pdfjs/ carries 171 real-world edge-case PDFs curated from
the mozilla/pdf.js test suite:
fuzzed crashers (poppler, pdfbox, ghostscript cases), lying xrefs and
page-tree /Counts, junk inside content streams, odd font programs,
filter and encryption corner cases. Where Ghent pins print-production
features, this corpus pins robustness. Per-file expectations record
which files open, which fail with a controlled exception, which need
passwords, and which legitimately render blank (see the README in the
corpus directory for provenance and per-file notes). Two layers again:
pdf_graphics/test/pdfjs_corpus_test.dart (pure-Dart open + interpret)
and dart_pdf_editor/test/pdfjs_render_test.dart (rasterization smoke over
the real decode pipeline, with optional PDF.js baseline comparison).
Checked-in PDF.js reference PNGs live under
test_corpora/pdfjs/_baselines, and the
latest checked-in side-by-side visual results are browsable from
test_corpora/pdfjs/_renders/README.md.
Run the checked-in corpora from their package directories so the relative
../../test_corpora/... paths line up:
- Ghent pure-Dart pass:
cd packages/pdf_graphics && fvm dart test test/ghent_corpus_test.dart - Ghent render/baseline pass:
cd packages/dart_pdf_editor && fvm flutter test test/ghent_render_test.dart - Accept intentional Ghent baseline changes:
cd packages/dart_pdf_editor && GHENT_UPDATE=1 fvm flutter test test/ghent_render_test.dart - Ghent visual review gallery:
cd packages/dart_pdf_editor && GHENT_RENDER_OUT=../../test_corpora/ghent/_renders fvm flutter test test/ghent_render_test.dart, then opentest_corpora/ghent/_renders/index.html - PDF.js pure-Dart pass:
cd packages/pdf_graphics && fvm dart test test/pdfjs_corpus_test.dart - PDF.js render smoke pass:
cd packages/dart_pdf_editor && fvm flutter test test/pdfjs_render_test.dart - PDF.js visual review gallery:
cd packages/dart_pdf_editor && PDFJS_RENDER_OUT=../../test_corpora/pdfjs/_renders fvm flutter test test/pdfjs_render_test.dart, then opentest_corpora/pdfjs/_renders/index.html - Generate PDF.js reference baselines:
cd packages/dart_pdf_editor/tool/pdfjs_baseline && npm install && npm run render - PDF.js pixel compare + side-by-side results:
cd packages/dart_pdf_editor && PDFJS_BASELINE_DIR=../../test_corpora/pdfjs/_baselines fvm flutter test test/pdfjs_render_test.dart, then opentest_corpora/pdfjs/_renders/index.html - Rebuild the checked-in PDF.js gallery index without rerendering:
fvm dart packages/dart_pdf_editor/tool/rebuild_pdfjs_render_index.dart - All checked-in corpus tests: run the four non-update test commands above
(excluding the visual galleries); they are intentionally split because
pdf_graphicsis VM-only anddart_pdf_editorneeds Flutter rasterization.
The Ghent gallery writes the same 2x rasters used for baseline comparison.
The PDF.js gallery writes the same pages as the smoke pass by default (up to
five per file at 1x); override it with PDFJS_RENDER_MAX_PAGES and
PDFJS_RENDER_PIXEL_RATIO when you need deeper or sharper review. When
PDFJS_BASELINE_DIR is set, the Flutter test compares Dart rasters against
the PDF.js baselines using PDFJS_COMPARE_CHANNEL_TOLERANCE (default 8) and
PDFJS_COMPARE_MAX_DIFF_FRACTION (default 0.0005), and writes
test_corpora/pdfjs/_renders/index.html unless PDFJS_RENDER_OUT overrides
the output directory. The results page shows each page as one row with the
PDF.js baseline, Dart render, and diff side by side.
If you have the private corpus/ directory locally, use it for broader
real-world coverage:
- Parse check:
cd packages/pdf_document && fvm dart tool/inspect.dart ../../corpus/*.pdf - Single render check:
cd packages/dart_pdf_editor && PDF_PATH=../../corpus/<file>.pdf PDF_PAGE=0 fvm flutter test test/render_smoke_test.dart - Full render sweep:
cd packages/dart_pdf_editor && CORPUS_DIR=../../corpus RENDER_OUT=../../corpus/renders fvm flutter test test/corpus_render_test.dart

