TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
Pure-Dart PDF renderer, viewer & editor for Flutter. View, annotate, OCR, fill forms, sign, redact & compare PDFs on iOS, Android, macOS, Windows, Linux & web. No native bindings.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
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.
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.README 전문 보기 ↗[!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. |
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 --json
The 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_editor
Then 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 web
pdf_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, inclusive
PdfEditorView(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.
appendPagesFrom), and split (extractPages writes
a standalone file; extracting from an encrypted document decrypts)saveSigned, adbe.pkcs7.detached, RSA-SHA256, verified
interoperable with OpenSSL and poppler)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; reflowText rewraps supported single-column paragraphs,
including compatible embedded Type0 fonts)Deliberately deferred: JBIG2 Huffman/refinement variants and JPX subsampling/PCRL-CPRL progressions.
PdfViewer.pageColor),
chrome theming via PdfViewerTheme, and a hide-all-annotations
toggle.PdfEditingController saves incrementally on every edit, so revisions
are byte prefixes of one buffer and undo/redo is essentially free.{{date}}-style fields still live.PdfViewer.highlightFormFields), so they're findable at a glance.PdfEditor.injectTextLayer writes already-recognized PdfOcrSpans 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 pluggable
PdfOcrEngine, maps raster boxes back through crop boxes and /Rotate, and
injects the layer for you.pdf_ocr_ondevice supplies a native offline engine backed by ONNX Runtime
and a downloadable PP-OCR model. pdf_ocr_vlm supplies an HTTP engine for
dots.ocr/vLLM, cloud VLMs, or a small adapter around Tesseract/PaddleOCR.
Hosts can also implement PdfOcrEngine directly.PdfSearchField and an editable PdfPageNumberField
("3 / 12") for app bars.PdfEditingPreferences, backed by
shared_preferences).PdfColorPicker) with hex/RGB/HSL/CMYK entry and
an eyedropper that samples from the rendered page.PdfAnnotationSnapshot serializes to JSON with the appearance stream
included, so a synced annotation renders identically on every device.annotationChanges emits per-revision diffs (created/modified/removed,
undo and redo included); applyRemoteChange replays 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.canEditAnnotation predicate 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 test
The 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.dart interprets every page on
the plain Dart VM (parse + paint-op assertions, no rasterization).dart_pdf_editor/test/ghent_render_test.dart rasterizes every page and
compares it pixel-wise against checked-in baseline renders;
regressions dump actual/diff images for inspection, and
GHENT_UPDATE=1 re-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:
cd packages/pdf_graphics && fvm dart test test/ghent_corpus_test.dartcd packages/dart_pdf_editor && fvm flutter test test/ghent_render_test.dartcd packages/dart_pdf_editor && GHENT_UPDATE=1 fvm flutter test test/ghent_render_test.dartcd packages/dart_pdf_editor && GHENT_RENDER_OUT=../../test_corpora/ghent/_renders fvm flutter test test/ghent_render_test.dart, then open test_corpora/ghent/_renders/index.htmlcd packages/pdf_graphics && fvm dart test test/pdfjs_corpus_test.dartcd packages/dart_pdf_editor && fvm flutter test test/pdfjs_render_test.dartcd packages/dart_pdf_editor && PDFJS_RENDER_OUT=../../test_corpora/pdfjs/_renders fvm flutter test test/pdfjs_render_test.dart, then open test_corpora/pdfjs/_renders/index.htmlcd packages/dart_pdf_editor/tool/pdfjs_baseline && npm install && npm run rendercd packages/dart_pdf_editor && PDFJS_BASELINE_DIR=../../test_corpora/pdfjs/_baselines fvm flutter test test/pdfjs_render_test.dart, then open test_corpora/pdfjs/_renders/index.htmlfvm dart packages/dart_pdf_editor/tool/rebuild_pdfjs_render_index.dartpdf_graphics is VM-only and dart_pdf_editor needs 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:
cd packages/pdf_document && fvm dart tool/inspect.dart ../../corpus/*.pdfcd packages/dart_pdf_editor && PDF_PATH=../../corpus/<file>.pdf PDF_PAGE=0 fvm flutter test test/render_smoke_test.dartcd packages/dart_pdf_editor && CORPUS_DIR=../../corpus RENDER_OUT=../../corpus/renders fvm flutter test test/corpus_render_test.dartdart_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. |