nigig-org/REVIEWS/DART_PDF_VS_MAKEPAD_PDF_GAP_ANALYSIS.md
2026-07-26 19:38:26 +03:00

51 KiB
Raw Permalink Blame History

dart-pdf vs makepad-pdf — Comprehensive Gap Analysis

Comparison of the dart-pdf (Dart/Flutter) library's public API against the makepad-pdf (Rust) implementation in makepad/libs/pdf_parse/src/.


1. PDF File Parsing & Structure

Feature dart-pdf Description Makepad Status What's Missing
CosDocument (PDF parsing) Full COS-level parser: xref tables, trailer, object loading, object streams, recovery of broken xref via header scanning EXISTSPdfDocument::parse() Object stream support is partial; xref recovery not fully equivalent
Xref table parsing Full traditional xref + cross-reference streams; incremental update support (applyIncrementalUpdate) EXISTSparse_xref() + xref module No xref-hybrid or xref-stream parsing; no incremental update merging
Linearized PDF Not explicitly surfaced but parseable MISSING
Object streams Compressed objects inside ObjStm are decoded on demand PARTIAL — parser can handle some stream objects Not full ObjStm indexed access
Incremental update CosIncrementalUpdater appends revision sections; PdfEditor.save() produces incremental bytes EXISTSPdfIncrementalUpdater + PdfWriter Limited to content stream replacement; no full dictionary-level incremental updates
PDF version handling version getter from %PDF- header; catalog /Version override PARTIAL — reads header
Byte-range random access PdfByteSource, PdfHttpByteSource for progressive/remote loading MISSING No remote/progressive byte source support
Token stream Full lexer with token types, position tracking EXISTSLexer
COS object model CosDictionary, CosArray, CosStream, CosString, CosName, CosInteger, CosReal, CosBoolean, CosNull, CosReference EXISTSPdfObj enum Similar model; fewer variant types
Serializer CosSerializer — writes any COS object to PDF syntax EXISTSPdfWriter
Document open recovery Full xref recovery by scanning for N G obj headers, fallback catalog discovery PARTIAL — basic parsing No header-scan recovery
Decoded-stream cache LRU cache for decoded streams with budget caps MISSING No decoded-stream caching

2. Content Stream Operations

Feature dart-pdf Description Makepad Status What's Missing
Content stream parser ContentStreamParser.parse() + cursor-based incremental parsing EXISTSparse_content_stream() No cursor/incremental parsing
PdfOp enum (all ops) All standard content ops: path construction, path painting, graphics state, color, text, XObjects, inline images, marked content EXISTSPdfOp enum Equivalent coverage
Path construction m, l, c, v, y, h, re EXISTS — all path ops
Path painting S, s, f/F, f*, B, B*, b, b*, n EXISTS — all painting ops
Clipping W, W* EXISTSClip, ClipEvenOdd
Graphics state q, Q, cm, w, J, j, M, d, gs EXISTS — all graphics state ops
Inline images BI/ID/EI — decoded by device EXISTSInlineImage variant
XObjects Do operator EXISTSPaintXObject
Marked content BMC, BDC, EMC EXISTS — all three operators
Compatibility BX, EX MISSING — skipped silently
Type3 metrics d0, d1 MISSING
Shading sh operator MISSING
Cancellation PdfCancellationToken for cooperative cancellation mid-walk MISSING
Async interpretation drawPageOperationsAsync(), drawPageContentAsync() with yield intervals MISSING

3. Graphics State (Interpreter)

Feature dart-pdf Description Makepad Status What's Missing
GraphicsState class Full state: CTM, fill/stroke color, stroke params, font, text state, color spaces, soft mask, blend mode, overprint, patterns PARTIALPdfGfxState has CTM, colors, stroke params, font, text matrix, char/word spacing No color spaces tracking, no soft mask, no blend mode, no overprint, no pattern state
Save/restore Stack-based q/Q with full state clone EXISTS — push/pop on state stack
CTM (transform) 3×3 matrix via cm operator EXISTSConcatMatrix
Clip path clipPath() device callback with fill rule EXISTSClip/ClipEvenOdd
Overprint setOverprint() with fill/stroke/mode MISSING
Blend mode setBlendMode() with full PdfBlendMode enum (16 modes) MISSING in content ops
Soft mask beginSoftMasked()/endSoftMasked() with luminosity mask, backdrop, transfer function MISSING

4. Path Construction & Painting

Feature dart-pdf Description Makepad Status What's Missing
PdfPath Sealed path segment hierarchy: PdfMoveTo, PdfLineTo, PdfCubicTo, PdfClosePath EXISTSPdfOp::MoveTo/LineTo/CurveTo/ClosePath No sealed type; segments are enum variants directly
PdfStroke Width, cap (0/1/2), join (0/1/2), miter limit, dash array, dash phase EXISTSSetLineWidth, SetLineCap, SetLineJoin, SetMiterLimit, SetDash
PdfFillRule nonzero, evenOdd EXISTS — separate ops Fill/FillEvenOdd
Ellipse approximation 4× Bézier quarter-arc with _kappa constant EXISTS — in content_writer.dart helper Not in makepad core
Rounded rect Full rounded-rect path via 4× Bézier + straight lines EXISTSPdfOp::RoundedRect
SVG path parsing Not in dart-pdf; separate EXISTSsvg_path.rsparse_svg_path() Makepad has this, dart-pdf doesn't
Gradient path fill fillPathGradient() with axial/radial shading patterns MISSING
Gouraud mesh fillMesh() for mesh shadings (types 47) MISSING

5. Text Operations

Feature dart-pdf Description Makepad Status What's Missing
Text state Tf, Td, TD, Tm, T*, TL, Tc, Tw, Tz, Ts, Tr EXISTS — all text state ops in PdfOp
Text showing Tj, TJ, ', " operators EXISTSShowText, ShowTextArray, ShowTextNextLine, ShowTextNextLineSpacing
PdfTextRun Rich text run: transform, color, gradient, font info, glyph outlines, invisible flag, stroke mode, MCID MISSING No text run abstraction; text is just bytes in content ops
PdfGlyphPlacement Per-glyph outline + offset in em units MISSING
Text extraction TextExtraction module, text_diff.dart, struct_text.dart MISSING
Text clipping Render modes 47: glyph outlines accumulate for clipping at ET MISSING
Vertical text Vertical writing mode support with offsetY MISSING
Font metrics measureHelvetica(), base-14 width tables EXISTSchar_width() in font.rs
ContentWriter text ops beginText(), endText(), font(), textAt(), showText(), showGlyphHex(), nextLine(), charSpacing(), horizontalScale() EXISTSPdfOp variants No ContentWriter abstraction yet

6. Color & Color Spaces

Feature dart-pdf Description Makepad Status What's Missing
PdfColor (RGB) PdfColor(r, g, b) with named constructors: gray(), cmyk(), black, white EXISTSPdfColor (RGBA f32)
DeviceGray Single-channel gray EXISTS — via PdfColor::gray()
DeviceRGB Standard RGB EXISTSPdfColor default
DeviceCMYK CMYK color space PARTIALSetStrokeCmyk/SetFillCmyk ops exist No CMYK→RGB conversion
CalGray CIE-based calibrated gray MISSING
CalRGB CIE-based calibrated RGB MISSING
Lab CIE L*a*b* MISSING
ICCBased ICC profile-based color management MISSING
Indexed Palette-based: 1 component selects from lookup table MISSING
Separation Single-colorant space with tint transform MISSING
DeviceN Multi-colorant space with tint transform MISSING
Pattern Pattern color space MISSING
PdfColorSpace parser PdfColorSpace.parse() resolves any CS object: device, calibrated, ICC, indexed, separation, deviceN, pattern MISSING
ICC profile IccProfile.parse() with channel count, sRGB conversion MISSING
Calibrated color PdfCalibratedColorSpace for CalGray/CalRGB/Lab MISSING
Function-based tint PdfFunction for Separation/DeviceN tint transforms MISSING
Color component parsing sc/scn/SC/SCN ops resolve through active color space PARTIALSetFillColor(Vec<f64>) raw No space-aware resolution
Overprint setOverprint() for subtractive color compositing MISSING
Color for annotations _colorArray() converts PDF color arrays (gray/RGB/CMYK) to 0xRRGGBB MISSING

7. Images

Feature dart-pdf Description Makepad Status What's Missing
PdfImageRequest Image draw request: stream, transform, alpha, stencil mode, inline flag, pre-decoded pixels MISSING
Image XObject parsing PdfImage with width, height, color space, bits per component, filters EXISTSPdfImage struct in image.rs
JPEG embedding PdfEmbeddableImage.jpeg() — passes DCT-encoded bytes through MISSING
PNG embedding PdfEmbeddableImage.png() — decodes PNG, re-compresses as Flate, splits alpha to /SMask MISSING
Image decode decodeImageToRgba() in image.rs EXISTS — for JPEG/PNG
Image extract extract_image() reads XObject streams EXISTS — in image.rs
Inline image decode _drawInlineImage() in interpreter MISSING in interpreter
Stencil mask /ImageMask stencils with stencilColor MISSING
Image scan pass scanImagesOnly mode: fast first pass to collect image sources MISSING
Image cache PdfDecodedPixels with pixel cache keyed by stream content MISSING
CMYK JPEG rejection CMYK JPEGs explicitly rejected for embedding MISSING
JPEG info reader readJpegInfo() — SOF marker parsing for dimensions/components MISSING
PNG decoder Full PNG decode with alpha extraction, tRNS palette support MISSING

8. Fonts

Feature dart-pdf Description Makepad Status What's Missing
PdfFontInfo Loaded font: glyph outlines, widths, encoding, CMap, vertical mode EXISTSfont.rs has char_width, glyph metrics
Type0 (composite) CJK fonts with CMap-based encoding MISSING
Type1 PostScript outline fonts MISSING
Type3 User-defined glyph procedures (d0/d1 metrics, CharProc content) MISSING
TrueType TrueType embedded fonts with glyph tables MISSING
CFF Compact Font Format outlines MISSING
Base-14 metrics Helvetica, Times, Courier (all variants) AFM widths hardcoded EXISTSchar_width() + metrics tables
Font embedder PdfFontEmbedder for embedding TTF/OTF fonts into PDF MISSING
ToUnicode CMap Maps glyph codes back to Unicode for text extraction MISSING
Shared font cache LRU cache (128 entries) across renders MISSING
Glyph outlines outlineFor() returns PdfPath for embedded font glyphs MISSING
Vertical writing isVertical flag from CIDFont WMode MISSING
Standard fonts enum PdfStandardFont — 12 base-14 variants with family/bold/italic EXISTSPdfStandardFont enum in makepad
Font name resolver PdfStandardFont.fromName() maps any font name to base-14 MISSING

9. Annotations

Feature dart-pdf Description Makepad Status What's Missing
PdfAnnotation base Full annotation: subtype, rect, flags, color, interior color, border, actions, appearance streams, opacity EXISTSPdfAnnotation in annotation.rs
PdfLinkAnnotation Clickable link with action (URI, GoTo, Named, JavaScript) EXISTS — link handling in annotation.rs
PdfWidgetAnnotation Form field widget with field type, name, value, action MISSING
Text markup Highlight, Underline, StrikeOut, Squiggly with QuadPoints EXISTSHighlight in editor.rs Partial — only highlight; no underline/strikeout/squiggly
Ink Freehand ink strokes (InkList) MISSING
FreeText Text annotations with /DA styling, callout lines, rich content (XHTML) MISSING
Square/Circle Rectangle and ellipse annotations EXISTSadd_square_annotation()
Line Line annotations with endpoints EXISTSadd_line_annotation()
PolyLine/Polygon Multi-vertex annotations MISSING
Stamp Stamp annotations (text stamps, image stamps, custom stamps, template stamps) PARTIALstamp_page() for text stamping No stamp annotation type; just content stream stamping
Measurement annotations Line/PolyLine/Polygon measurements with /Measure scale, takeoff metadata MISSING
Redaction Redaction annotations MISSING
Annotation behavior PdfAnnotationBehavior — editability, tap targets, style resolution MISSING
Annotation editing PdfAnnotationEditing — move, resize, restyle, rotate annotations MISSING
Appearance stream rendering drawAnnotations() with fallback rendering for every annotation type MISSING
Annotation clipboard Copy/paste annotations across pages/documents MISSING
Annotation sync Cross-device annotation synchronization MISSING
Comments/threads PdfCommentThread — reply threads, state annotations MISSING

10. Form Fields

Feature dart-pdf Description Makepad Status What's Missing
PdfAcroForm Full AcroForm: fields tree, default appearance, NeedAppearances EXISTSPdfFormEditor in form.rs
PdfFormField Terminal field: name, type, value, checked state, widgets, options, /DA, flags EXISTSPdfFormField in form.rs
PdfFieldType text, checkBox, radioGroup, pushButton, comboBox, listBox, signature EXISTSPdfFieldType in form.rs
Field value reading value, isChecked, options getter EXISTSget_field_value()
Field value writing PdfFormEditor.setFieldValue() with appearance regeneration EXISTSset_field_value()
Widget reconciliation _reconcileOrphanWidgets() — matches page widgets not in /Fields MISSING
Form appearance regen _regenerateFormAppearancesOnPage() after edits MISSING
Form admin form_admin.dart — batch field operations, field metadata MISSING
Form styling form_styling.dart — font/color/border styling across fields MISSING
Signature field PdfFieldType.signature MISSING
Radio on-states onStates, widgetOnState() for radio button groups MISSING

11. Digital Signatures

Feature dart-pdf Description Makepad Status What's Missing
PdfSignature Full signature: signer, reason, location, byte range, contents, subFilter, signing time EXISTSPdfSignature in signature.rs
Signature validation validate() with CMS parse, digest check, crypto verify, byte range check MISSING
PAdES levels B-B, B-T, B-LT, B-LTA detection from CAdES markers + timestamp + DSS EXISTSPdfPadesLevel in signature.rs
CMS/PKCS#7 parse CmsSignedData.parse(), CmsSignerInfo, certificate extraction MISSING
CMS verification cmsVerify() — digest match + RSA/ECDSA signature verify MISSING
X.509 certificate X509Certificate.parse() — full TBS, extensions, OCSP/CRL URLs MISSING
Certificate chain building verifyCertificateChain() — leaf to trust anchor path MISSING
Trust store PdfTrustStore — add DER/PEM certificates MISSING
RSA operations RSA sign, verify, PKCS#1 v1.5, PSS MISSING
ECDSA operations ECDSA sign/verify with curve support MISSING
Timestamp tokens TimeStampToken.parse(), RFC 3161 validation MISSING
DSS (Document Security Store) PdfDss.of() — read /Certs, /OCSPs, /CRLs from catalog MISSING
Revocation status PdfRevocationStatus — none/unknown/good/revoked from embedded OCSP/CRL MISSING
Revocation client PdfRevocationClient typedef for fetching OCSP/CRL MISSING
Timestamp client PdfTimestampClient typedef for TSA requests MISSING
Signing identity PdfSigningIdentity — self-signed CA generation, certificate creation MISSING
Fulcio fulcio.dart — Sigstore/Fulcio identity integration MISSING
X509 builder x509_builder.dart — CA certificate generation, cert signing MISSING
ASN.1 asn1.dart — DER parse/serialize, sequence, set, context tags MISSING
AES encryption AES-128, AES-256 CBC encrypt/decrypt for signatures EXISTS — in encryption.rs
RC4 encryption RC4 stream cipher for classic encryption EXISTS — in encryption.rs
CRL parsing CertificateRevocationList.parse() MISSING
OCSP parsing OcspResponse.parse() MISSING
ESS signing certificate essSigningCertificateV2Attribute() — PAdES baseline marker MISSING
Doc timestamp validation _validateDocTimeStamp() — full RFC 3161 token check MISSING
Byte-range signature Full ByteRange + Contents handling with gap validation MISSING

12. Encryption & Security

Feature dart-pdf Description Makepad Status What's Missing
StandardSecurityHandler V1V5, R2R6: RC4 40/128-bit, AES-128, AES-256, password auth EXISTSPdfEncrypt in encryption.rs
RC4 RC4 40-bit and 128-bit encryption/decryption EXISTSrc4.rs
AES-128 AES CBC with per-object keys EXISTSaes.rs
AES-256 AES-256 with SHA-256/384/512 hash-2B algorithm (R5/R6) EXISTSPdfEncrypt supports AES-256
Password auth User password, owner password, Algorithm 27 EXISTSauthenticate()
Permission flags /P permission word handling EXISTSPermission struct
Encrypt metadata /EncryptMetadata flag — exempt /Metadata streams MISSING
Crypt filter /Crypt filter with Identity bypass MISSING
Object-level key derivation Algorithm 1: file key + obj num + gen + salt EXISTS — per-object key derivation
Encrypt on write encryptObjectGraph() — deep-copy with encryption MISSING
Decrypt on read decryptObjectGraph() — in-place decryption of string graph MISSING
Stream payload encryption streamPayloadIsEncrypted() with XRef/Metadata/Crypt exemptions MISSING

13. Page Management

Feature dart-pdf Description Makepad Status What's Missing
PdfPage MediaBox, CropBox, rotation, resources, annotations, content bytes EXISTSPdfPage in page.rs
Page tree Full page tree walk with inherited attributes, /Count hints, leaf caching PARTIAL — basic page tree No inherited attribute merging; no /Count skip optimization
Page count pageCount from leaf walk (not /Count) EXISTSpage_count()
MediaBox/CropBox Resolved through tree with US Letter fallback EXISTSmedia_box CropBox not explicitly surfaced
Rotation Inherited rotation, normalized to 0/90/180/270 EXISTSrotate field
Content bytes contentBytes() — concatenates decoded /Contents streams EXISTScontent_bytes()
Page cache _pageCache by index, invalidatePageCache() on structural edits MISSING
Page labels page_labels.dart — numbered page labels from /PageLabels MISSING
Page format page_format.rs — page creation with format presets EXISTS
Page operations page_ops.rs — page manipulation operations EXISTS
Multi-page multi_page.rs — multi-page support EXISTS

14. Document Metadata & Info

Feature dart-pdf Description Makepad Status What's Missing
Document info /Title, /Author, /Subject, /Keywords, /Creator, /Producer EXISTSPdfDocumentInfo in document_info.rs
setInfo() PdfEditor.setInfo() — write/update document info dictionary MISSING
XMP metadata xmp.dart — XMP packet reading/writing MISSING
Document ID Trailer /ID array EXISTS — in encryption
Trailer Full trailer dictionary access EXISTStrailer field
Version %PDF- header version + catalog /Version PARTIAL — header only

15. Bookmarks & Outlines

Feature dart-pdf Description Makepad Status What's Missing
PdfOutline Read outline tree: items, destinations, open/closed, bold/italic/color EXISTSPdfBookmarkTree in outline.rs
PdfOutlineItem Title, destination, open state, children, bold, italic, color EXISTSPdfBookmarkItem in outline.rs
PdfDestination Page + fit type (Fit, FitH, FitV, FitR, XYZ) with params EXISTSPdfOutlineDestination
PdfExplicitDestination Write-side destination builder MISSING
Outline editing PdfOutlineEditing — add, remove, reorder, restyle items MISSING
Named destinations /Dests dictionary and /Names → /Dests name tree MISSING

16. Transparency & Compositing

Feature dart-pdf Description Makepad Status What's Missing
Blend modes 16 PDF blend modes (normal, multiply, screen, overlay, darken, lighten, etc.) EXISTSBlendMode enum in transparency.rs
Blend pixel blend_pixel() — per-pixel compositing with blend modes EXISTS — in transparency.rs
Transparency group TransparencyGroup — group compositing EXISTS — in transparency.rs
Soft mask SoftMask struct EXISTS — in transparency.rs
beginGroup/endGroup PdfDevice.beginGroup(alpha, knockout) / endGroup() MISSING
beginSoftMasked/endSoftMasked Offscreen capture with luminosity mask, backdrop luminance, transfer function MISSING
Alpha Per-element alpha in fillPath() and strokePath() EXISTS — alpha in PdfColor (RGBA)
Overprint mode /OPM 0 or 1 MISSING

17. Shading & Gradients

Feature dart-pdf Description Makepad Status What's Missing
Axial shading Linear gradient shading patterns EXISTSGradient::Linear in gradient.rs
Radial shading Radial gradient shading patterns EXISTSGradient::Radial in gradient.rs
Mesh shadings (types 47) Free-form Gouraud, Coons, tensor-product mesh shadings MISSING
PdfGradient Axial/radial gradient with color stops, averageColor fallback MISSING
PdfMesh Gouraud triangle mesh with averageColor MISSING
Shading function Function-based shadings MISSING
Tiling patterns PaintType 1 patterns with cell content MISSING
build_gradient_ops gradient.rs — generate PDF ops for gradient fill EXISTS
Tile modes TileMode enum (clamp, repeat, reflect) EXISTS — in gradient.rs
Function parsing PdfFunction for type 2 (exponential) and type 3 (stitching) MISSING

18. Filters & Compression

Feature dart-pdf Description Makepad Status What's Missing
FlateDecode zlib decompression EXISTS — in filter.rs
LZWDecode LZW decompression EXISTSfilter_lzw.rs
RunLengthDecode Run-length decompression EXISTSfilter_runlength.rs
CCITTFaxDecode Group 3/4 fax decompression EXISTSfilter_ccitt.rs
DCTDecode JPEG passthrough (no decode needed for embedding) EXISTSPdfFilter::Dct
ASCII85Decode ASCII85 decompression MISSING
ASCIIHexDecode ASCII hex decompression MISSING
Crypt filter /Crypt filter for decryption passthrough MISSING
Filter chain Multiple filters in sequence EXISTS — filter array parsing
DecodeParms Filter parameters handling PARTIAL
Encode (write) FlateEncode for embedding content/images EXISTSPdfFilter::Flate encode

19. Editing & Writing

Feature dart-pdf Description Makepad Status What's Missing
PdfEditor High-level editing session: accumulate edits, save as incremental update EXISTSPdfEditor in editor.rs
CosIncrementalUpdater Append-only incremental revision builder EXISTSPdfIncrementalUpdater in writer.rs
replace_page_content Replace page content stream ops EXISTSreplace_page_content()
stamp_page Stamp text on page EXISTSstamp_page()
add_highlight Add highlight annotation EXISTSadd_highlight()
add_square_annotation Add square annotation EXISTSadd_square_annotation()
add_line_annotation Add line annotation EXISTSadd_line_annotation()
ContentWriter Operator-by-operator content stream builder MISSING (no equivalent abstraction)
Form editing PdfFormEditor.setFieldValue() with appearance regen EXISTSset_field_value()
Page rotation rotatePage() with form appearance regen MISSING
Annotation editing Move, resize, rotate, restyle, delete annotations MISSING
Free text editing Create/edit free-text annotations with styling MISSING
Ink editing Create/edit freehand ink strokes MISSING
Image stamp addImageStamp() — embed image as stamp annotation MISSING
Stamp templates PdfStampTemplate — reusable stamp templates MISSING
Outline editing Add, remove, reorder bookmarks MISSING
Page editor Add, remove, reorder, duplicate pages MISSING
Content editor Modify base page content (reflow, rewrite operators) MISSING
Redaction Apply redaction annotations (whiteout) MISSING
OCR editor ocr_editor.dart — OCR integration for scanned pages MISSING
Attachment editor Add/remove file attachments MISSING
Header/footer header_footer.dart — add headers/footers across pages MISSING
Save as bytes save() → full file bytes; saveTail() → incremental append only EXISTSsave() → bytes
Undo support save_state() before edits for undo EXISTSsave_state()
Impact tracking PdfEditImpact — tracks which pages changed visually/content/annotations MISSING
Struct tree editing writeStructTree() — write tagged PDF structure MISSING
Page labels editing Edit page label numbers and prefixes MISSING

20. Accessibility & Conformance

Feature dart-pdf Description Makepad Status What's Missing
StructTree PdfStructTree — full structure tree: role map, elements, reading order MISSING
PdfStructElement Structure type, alt text, actual text, language, children, MCID refs MISSING
PdfStructParentTree Number tree mapping MCIDs to structure elements MISSING
PdfStructSpec Write-side structure spec for tagging content MISSING
Tagged PDF pdfIsMarkedTagged() — check /MarkInfo /Marked MISSING
PDF/UA pdf_ua.dart — PDF/UA-1 validation checker MISSING
PDF/A pdf_a.dart — PDF/A-2b validation checker MISSING
Conformance report PdfConformanceReport with error/warning/info findings by rule MISSING
Alt text Structure element /Alt for figures and non-text content MISSING
Actual text /ActualText for ligature/abbreviation expansion MISSING
Reading order elementsInReadingOrder() — depth-first pre-order tree walk MISSING
Object references /OBJR — structure element to whole-object reference MISSING
MCID tagging Marked-content IDs linking content to structure MISSING
Document AI document_ai.dart — AI-assisted document analysis MISSING

Summary of Makepad Strengths (not in dart-pdf)

Feature Makepad Status dart-pdf Status
SVG path parsing svg_path.rs — full SVG path to PDF ops Not present
Barcode generation barcode.rs — Code128, Code39, EAN13, QR code Not present
Table layout table_layout.rsTableStyle, render_table() Not present
Watermark watermark.rs — watermark builder API Not present
Rounded rect PdfOp::RoundedRect — built-in op ContentWriter helper only
Gradient generation build_gradient_ops() in gradient.rs Not in public API
PDF version from bytes Reads %PDF- header directly

Priority Gaps (Highest Impact)

  1. Text extraction / PdfTextRun — makepad has no equivalent of dart-pdf's rich PdfTextRun with glyph outlines and MCID tagging
  2. Color space system — dart-pdf has a complete PdfColorSpace parser for all 10+ families; makepad has none
  3. Signature validation — dart-pdf has full CMS/X.509/PAdES/chain verification; makepad only has the signature struct
  4. Accessibility/Tagged PDF — dart-pdf has PdfStructTree, PdfStructElement, PDF/UA, PDF/A validation; makepad has none
  5. Form widget rendering — dart-pdf renders widget annotations; makepad lacks this
  6. ICC profiles — dart-pdf has IccProfile.parse() with sRGB conversion; makepad has none
  7. Transparency compositing — dart-pdf's beginGroup/beginSoftMasked with offscreen capture; makepad has basic blend modes only
  8. Mesh shadings — dart-pdf handles types 47 mesh shadings; makepad has none
  9. Annotation rendering — dart-pdf renders all annotation types with fallbacks; makepad only annotates via content ops
  10. XMP metadata — dart-pdf has XMP read/write; makepad has none

Unified Execution Plan: Port dart-pdf Architecture into Makepad

Synthesis of Three Reviews

All three reviews converge on the same diagnosis: we shipped a feature dump, not an engine. The code has ~39K lines of PDF modules that are largely disconnected from the 1,200-line viewer widget. The path forward is not "fix the renderer" — it's adopt dart-pdf's architectural boundaries and build one vertical slice to proof.


Phase 0: The Purge (Days 13)

Goal: One renderer. No fake APIs. No orphan modules. Clean baseline.

Action Target Rationale
Archive current branch git tag archive/pdf-experiment-$(date +%Y%m%d) Preserve work without blocking
Start new branch from pinned dev b41e7404b6bb893d32f7d2924cd7b1c28983547f Clean baseline
Delete PdfFormFilling no-op methods form.rs Ok(()) that mutates nothing is a lie to callers
Delete empty render_annotations / render_form_fields / redraw_all stubs pdf_view.rs Dead integration seams
Delete or feature-gate hand-rolled crypto encryption.rs (MD5/SHA/AES/RC4), cms.rs CVE factory; replace with ring/sha2/aes when signatures are actually needed
Delete orphan wiring files (or keep behind pdf_wiring feature) form_widget_ui.rs, pdf_view_wiring.rs, inline_image_render.rs, annotation_click_handler.rs Reference code that compiles but does nothing
Delete all 40 new parser modules that the viewer doesn't call jbig2, trust_store, cms, ocr, stamp_annotation, redaction_annotation, measurement_annotations, etc. Feature accretion without integration
Choose one CachedPage struct and delete the other pdf_view.rs DRY violation
Remove create_texture_from_pdf_image duplicates (keep one in makepad_pdf_device.rs) inline_image_render.rs, pdf_view_wiring.rs Copy-pasted in 3 files
Record toolchain + Makepad commit + build commands Makefile or AGENTS.md Reproducible builds

Exit criterion: cargo check -p makepad-pdf-parse and cargo check -p makepad-widgets --features pdf pass with zero behavior changes. No API surface claims features it doesn't deliver.


Phase 1: Architecture — Split the Monolith (Days 410)

Goal: Mirror dart-pdf's layered crate structure. Strict dependency boundaries.

Target crate layout

makepad-pdf-cos          ← lexer, parser, filters, xref, COS objects
                            NO makepad_draw imports
                            NO rendering types

makepad-pdf-document     ← page tree, annotations, AcroForm model,
                            destinations, object references
                            NO makepad_draw imports
                            NO content interpreter

makepad-pdf-graphics     ← content interpreter, PdfDevice trait,
                            GraphicsState, fonts, image decode,
                            text extraction, RenderCommand recording
                            NO makepad_draw imports
                            Engine-neutral

makepad-pdf-makepad      ← MakepadPdfDevice (implements PdfDevice),
                            GPU textures, PdfView widget,
                            input routing, viewport, caching
                            THIS is where makepad_draw lives

optional: makepad-pdf-signing, makepad-pdf-ocr (separate repos/branches)

Dependency rules (non-negotiable)

  • cos → standalone Rust crate
  • document → depends on cos only
  • graphics → depends on cos + document only
  • makepad integration → depends on all above + makepad_draw
  • UI never mutates COS dictionaries directly. It submits typed document edits.
  • PdfDevice cannot expose Makepad types. Makepad implements it in the integration crate.

What moves where

Current location New crate Notes
object.rs, stream.rs, filter.rs, xref.rs, lexer.rs pdf-cos Pure parsing, no UI
document.rs, page.rs, annotations.rs, form.rs (model only), destinations.rs pdf-document Document semantics
device.rs, content.rs, font*.rs, image.rs, color_space.rs, graphics_state.rs, pattern.rs, transparency.rs, clipping.rs pdf-graphics Engine-neutral
makepad_pdf_device.rs, pdf_view.rs pdf-makepad Widget + GPU
encryption.rs, signature_crypto.rs, trust_store.rs, cms.rs delete or quarantine Not real implementations

RecordingDevice for testing

Before writing MakepadPdfDevice, create a RecordingDevice that captures all PdfDevice calls as structured RenderCommand values. This lets you:

  • Golden-test the interpreter output against a corpus
  • Verify operator sequencing without GPU context
  • Compare against dart-pdf's interpreter output

Exit criterion: Four crates compile independently. pdf-graphics has zero Makepad imports. RecordingDevice captures ops from interpret_ops on a test PDF.


Phase 2: Rendering Pipeline — MakePdfDevice the Only Renderer (Days 1120)

Goal: Delete the inline renderer. Wire the device as the sole rendering path. Fix the lies.

Step 2.1: Wire MakepadPdfDevice into PdfPageView

  1. Move render_page_via_device from the wiring file into the real impl PdfPageView block in pdf_view.rs
  2. Delete from pdf_view.rs:
    • Private GfxState struct
    • render_vectors / render_text methods
    • draw_one_text / pick_draw_text helpers
    • mul_mat / text_advance / cmyk_to_rgb / color_from_vals functions
    • collect_text_segments (keep for text selection, re-implement as post-device pass)
  3. The widget's draw_walk calls render_page_via_device which instantiates MakepadPdfDevice, feeds it the cached Vec<RenderCommand>, and drives rendering

Step 2.2: Introduce RenderCommands

Replace direct PdfOp replay with an intermediate layer:

PdfOps (PDF-native)
    ↓ interpret_ops + RecordingDevice
RenderCommand (engine-neutral)
    ↓ MakepadPdfDevice
Makepad GPU primitives

RenderCommand variants:

  • SaveState / RestoreState
  • SetTransform([f64; 6])
  • SetStrokeColor(PdfColor) / SetFillColor(PdfColor)
  • SetLineWidth(f64) / SetLineCap(i32) / SetLineJoin(i32)
  • MoveTo(f64, f64) / LineTo(f64, f64) / CurveTo(...) / ClosePath
  • Stroke / Fill / FillEvenOdd / StrokeClose
  • Clip(Path)
  • BeginText / SetFont(String, f64) / ShowText(Vec<u8>) / EndText
  • PaintImage { texture_key, ctm: [f64; 6] }
  • PaintFormXObject { name, ctm: [f64; 6] }

Cache Vec<RenderCommand> per page in CachedPage. Replaying cached commands is faster than re-parsing content streams.

Step 2.3: Fix the lies

Bug Fix
Inline images silently dropped RecordingDevice captures InlineImage ops; MakepadPdfDevice renders them
paint_x_object ignores rotation/skew Use full 6-component CTM bounding-box transform (all 4 corners)
clip() stores path then clears it Implement scissor rect for axis-aligned clips; stencil for complex paths
XObject not decoded on cache miss Decode on worker thread, store BGRA in CachedPageExt::image_data, create Texture on UI thread
set_dash is TODO Map PDF dash array to line stipple or shader uniform
Color space is TODO Route through color_space.rs; support DeviceGray/RGB/CMYK + ICCBased minimum

Exit criterion: One PDF with text, vectors, and images renders identically through both RecordingDevice (golden test) and MakepadPdfDevice (visual check). Old inline renderer code is deleted.


Phase 3: Document Model — Real State, Not Stubs (Days 2130)

Goal: Document-owned mutable state. Typed edits. No Ok(()) lies.

Step 3.1: Object references and identity

  • Pages, annotations, and form fields must be keyed by object reference (generation + offset), not name strings
  • Resolve inherited field keys up /Parent chains in AcroForm
  • Preserve widget↔field object identity across parse

Step 3.2: Annotation model

  • Parse all annotation types from page dictionaries
  • Resolve page references correctly (never default to page 0)
  • Store annotations in document-owned Vec<PageAnnotation> per page
  • Background loader sends real annotation data, not empty vectors

Step 3.3: Form field model

  • Parse AcroForm field tree with /FT, /Ff, /V, /AS, /DV
  • Resolve inherited values from parent fields
  • Distinguish text / checkbox / radio / choice / signature
  • Track dirty state per field
  • Delete PdfFormFilling and replace with DocumentFormEditor that:
    • Takes typed edit commands (SetText { field_ref, value }, SetCheck { field_ref, on/off })
    • Mutates document-owned state
    • Returns Result<(), FormError> with real error types
    • Invalidates affected page appearance

Step 3.4: Appearance generation (document-level)

  • Regenerate field appearances as a document operation (not widget code)
  • Generate minimal appearance streams for text fields and checkboxes
  • Test: edit → regenerate appearance → serialize → reparse → verify roundtrip

Exit criterion: Parse a real AcroForm PDF, modify a text field value via DocumentFormEditor, verify the field's /V changed and the dirty flag is set. No save/write yet — just in-memory correctness.


Phase 4: Interactivity — Wire the Orphan Files (Days 3138)

Goal: PdfPageView responds to user input. Forms work. Links navigate.

Step 4.1: Enable interaction

fn is_interactive(&self) -> bool { true }

fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
    // 1. Form field events (focus, keyboard, click)
    if handle_form_field_event(cx, event, &mut self.form_focus, ...) {
        self.redraw_all();
        return;
    }
    // 2. Annotation hit testing (links, buttons)
    if let Some(action) = annotation_click_handler::hit_test(event, &self.annotations, ...) {
        self.action_tx.send(action); // emit to host
        return;
    }
    // 3. Text selection (drag to select, copy)
    // 4. Viewport (zoom, pan, scroll)
}

Step 4.2: Annotation rendering

  • Draw link affordances (blue underline or rect)
  • Draw form field widget borders
  • Draw FreeText backgrounds
  • Cursor feedback: pointer over links, I-beam over text fields

Step 4.3: Form rendering

  • Render field values inside text field rects
  • Show focused field highlight
  • Render combo dropdown overlay
  • On value change → DocumentFormEditor → invalidate appearance → redraw

Step 4.4: Navigation actions

  • Emit typed actions to host: PdfAction::OpenUri(String), PdfAction::GoTo { page, dest }, PdfAction::Launch { file }
  • Viewer never opens URLs/browsers itself. Host application receives actions.

Exit criterion: Click a link in a real PDF → host receives OpenUri. Click a text field → type text → field value updates → appearance regenerates → visual change visible.


Phase 5: Font Engine & Text — Replace Magic with Metrics (Days 3948)

Goal: Correct font metrics. Proper text selection. Real glyph advances.

Step 5.1: Font metrics

  • Remove fs * 0.75 and fs * 0.8 magic numbers
  • Use actual font ascent/descent/cap-height from font dictionaries
  • For Type1: parse AFM or font dictionary metrics
  • For TrueType/CFF: read hhea, OS/2 tables

Step 5.2: Text selection

  • Replace byte-proportional width with accumulated glyph advances
  • Cache GlyphAdvanceCache per page (keyed by font name + size)
  • Rebuild only when page data changes, not every frame
  • Use char_width() from font.rs with proper font scaling

Step 5.3: Composite fonts

  • CMap parsing for CID fonts (identity, GBK, UniCNS, etc.)
  • ToUnicode map for text extraction
  • Two-byte character handling in text_advance

Step 5.4: Text extraction API

  • PageText::text_segments() -> Vec<TextSegment> with position data
  • PageText::find(query: &str) -> Vec<Match> with page/rect coordinates
  • Feed selection/copy/search from this API

Exit criterion: Select text across a line with mixed fonts. Verify selection rectangles align with actual glyph positions. Copy produces correct Unicode. Search finds a string and highlights the right page/position.


Phase 6: Testing Infrastructure — Earn the Green Checkmarks (Days 4956)

Goal: Real PDFs. Real regressions. No test theater.

Step 6.1: PDF corpus

Check into repo:

tests/fixtures/
  basic/          ← text-only, vector-only, simple images
  fonts/          ← Type1, TrueType, CID, composite, CJK
  forms/          ← text field, checkbox, radio, combo
  annotations/    ← link, FreeText, widget, popup
  images/         ← JPEG, PNG, inline, XObject
  edge/           ← rotation, crop boxes, nested CTMs
  malformed/      ← truncated, corrupt xref, bad streams
  encrypted/      ← RC4, AES (once real crypto is added)

Source: Ghent PDF Output Suite (subset), PDF.js edge-case corpus, hand-crafted minimal PDFs.

Step 6.2: Integration tests

#[test]
fn renders_text_vectors_images() {
    let doc = PdfDocument::parse(include_bytes!("fixtures/basic/hello.pdf"));
    let page = doc.page(0);
    let ops = parse_content_stream(&page.content_data);
    let mut recorder = RecordingDevice::new();
    let mut state = PdfGfxState::default();
    interpret_ops(&mut recorder, &ops, &mut state);
    assert!(!recorder.commands.is_empty());
    assert!(recorder.commands.iter().any(|c| matches!(c, RenderCommand::ShowText(_))));
}

Step 6.3: Fuzz testing

cargo fuzz run parse_content_stream -- -max_total_time=300
cargo fuzz run parse_xref -- -max_total_time=300
cargo fuzz run parse_object -- -max_total_time=300

Target: zero panics on arbitrary input. All errors are Result::Err, never unwrap on untrusted data.

Step 6.4: Visual regression (optional, later)

  • Serialize Vec<RenderCommand> as golden files
  • Compare across runs for deterministic output
  • Later: pixel comparison against PDFium baseline

Exit criterion: cargo test runs corpus tests. cargo fuzz runs for 5 minutes without panics. CI produces pass/fail report.


Phase 7: Performance — After Correctness (Days 5764)

Goal: Make it fast enough to ship. Not before correctness is proven.

Step 7.1: Off-thread parsing

  • Wire load_pdf_data_off_thread into the real widget lifecycle
  • Show loading placeholder during parse
  • Generation IDs on worker results (stale results cannot update new document)

Step 7.2: Texture management

  • LRU cache for decoded image textures
  • Evict textures for pages that scroll out of view
  • GPU upload only on UI thread

Step 7.3: Render command cache

  • Cache Vec<RenderCommand> per page in CachedPage
  • Replaying cached commands skips re-parsing content streams
  • Dirty-region rendering for interactive edits (form field changes)

Step 7.4: Progressive loading (stretch)

  • Port dart-pdf's PdfByteSource concept: random-access byte reads
  • First-paint: parse xref + render page 1 while rest downloads
  • Bounded fetch with cancellation

Exit criterion: Large PDF (50+ pages) loads and renders first page in <200ms. Rapid document switching doesn't leak memory or show stale pages.


Phase 8: Advanced Features — Separate Projects

Do not start any of these until Phases 07 are complete and tested.

Each gets its own branch, design doc, and merge criteria:

Feature Prerequisites Estimated effort
Encryption (RC4/AES-128/256) Real crypto crates (ring, aes), byte-range tests 23 weeks
Signatures CMS/X.509 via x509-cert + rsa/p256, trust store, interop tests against OpenSSL 46 weeks
Annotation editing Document model, appearance generation, incremental save 34 weeks
AcroForm full support Document model, appearance regeneration, JavaScript actions 46 weeks
Incremental save Writer/serializer, byte-preserving tests, signature preservation 34 weeks
Advanced color (ICC, DeviceN, Separation) ICC profile parser, Lab/XYZ conversion, tint transforms 34 weeks
Advanced transparency (blend modes, soft masks, groups) Render graph, compositing, GPU blending 46 weeks
JBIG2 / JPX External decoder crates or FFI, test fixtures 23 weeks each
OCR Host opt-in only, tesseract/ONNX integration, API key policy 23 weeks
PDF/UA structure tree Tagged PDF model, accessibility tree 34 weeks

Non-Negotiable Rules (All Phases)

  1. One user-visible capability per PR. No multi-feature dumps.
  2. No empty integration methods or Ok(()) no-ops in exposed APIs.
  3. No TODO that substitutes a fake default (page-0 fallback, empty forms, unchanged redaction).
  4. Parser has no makepad_draw dependency. Widgets own event routing and drawing.
  5. Viewer never launches commands/URLs. Host receives typed actions.
  6. Unsupported operations return typed errors, not silent degradation.
  7. Every feature PR includes: at least one real-PDF regression fixture + one behavior test.
  8. Apache-2.0 attribution. If dart-pdf code is ported: THIRD_PARTY_NOTICES.md, port ledger per file, legal review before distribution.

Phase Summary

Phase Duration Deliverable Success Metric
0: Purge Days 13 Clean baseline, no lies cargo check passes, no behavior changes
1: Architecture Days 410 4-crate split, RecordingDevice pdf-graphics compiles without makepad_draw
2: Rendering Days 1120 Single renderer, RenderCommands Real PDF renders through device only
3: Document Model Days 2130 Typed edits, real form state Edit field → verify /V changed
4: Interactivity Days 3138 Forms, links, navigation work Click link → host action; type in field → visible
5: Font Engine Days 3948 Correct metrics, text selection Select/copy text across mixed fonts
6: Testing Days 4956 Corpus, fuzz, CI cargo fuzz runs 5min without panic
7: Performance Days 5764 Off-thread, caching, texture LRU First page <200ms, no memory leak
8: Advanced Separate branches Each feature independently Per-feature design doc + tests

Total to a shippable v1 read-only viewer: ~64 working days.

The dart-pdf lesson: Port the architecture, contracts, and test strategy — not the files. A read-only viewer with correct rendering, working forms, and clickable links is a real product. 40 disconnected modules around a broken renderer is not.