nigig-org/REVIEWS/PLAN_INTERPRETER_1TO1_PORT.md
andodeki ac8f8aa002
Some checks failed
p2p-intel / engine (push) Waiting to run
p2p-intel / notifications (push) Waiting to run
p2p-intel / coverage (push) Waiting to run
p2p-intel / makepad-app (push) Waiting to run
p2p-intel / exchange-tab (push) Waiting to run
Payment domain, storage, platform and UI / isolated-payment-tests (push) Waiting to run
Payment domain, storage, platform and UI / payment-ui-tests (push) Waiting to run
repo hygiene / hygiene (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-build (CAD) / cad-engine-coverage (push) Has been cancelled
nigig-build (CAD) / doc-workspace-coverage (push) Has been cancelled
nigig-build (CAD) / cad-widget-coverage (push) Has been cancelled
traffic / gates (push) Has been cancelled
traffic / nigig-traffic (push) Has been cancelled
traffic / supply-chain (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / coverage (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
email / gates (push) Has been cancelled
email / email-domain (push) Has been cancelled
email / nigig-email (push) Has been cancelled
email / supply-chain (push) Has been cancelled
nigig-map / test (push) Has been cancelled
sms / gates (push) Has been cancelled
sms / robius-sms (push) Has been cancelled
sms / android (push) Has been cancelled
sms / nigig-sms (push) Has been cancelled
sms / supply-chain (push) Has been cancelled
spreadsheet / engine-coverage (push) Has been cancelled
spreadsheet / ui-controller-coverage (push) Has been cancelled
chore: sync full working tree to gitdab
Whole-tree sync: cad-core/cad-ui split sources, nigig-build
construction_frame migration, pdf port progress, mpesa/pay/uikit/doc
updates, workspace members/profiles/lock, CI workflows and reviews.
See individual file history for details.
2026-09-12 07:15:24 +03:00

6 KiB

Plan: 1:1 Port of dart-pdf interpreter_test.dartnigig-pdf (Phase 2.5)

Date: 2026-09-06 Reference: dart-pdf/packages/pdf_graphics/test/interpreter_test.dart (1885 LOC), dart-pdf/packages/pdf_graphics/test/streaming_interpreter_test.dart Nigig baseline: crates/apps/pdf/pdf-graphics/src/content.rs (interpreter), crates/apps/pdf/pdf-graphics/src/recording.rs (RecordingDevice), crates/apps/pdf/pdf-graphics/src/device.rs (PdfDevice) Master plan: NIGIG_PDF_FEATURE_PARITY_PLAN.md Phase 2 (interpreter_test / streaming_interpreter_test | P2)


1. Gap Audit

Aspect dart interpreter_test device nigig RecordingDevice Gap
Path API fillPath(PdfPath path, PdfColor color, PdfFillRule rule, double alpha) , strokePath(PdfPath, PdfColor, PdfStroke, alpha) , clipPath(PdfPath, PdfFillRule) — path is a single PdfPath object plus style move_to(x,y), line_to, curve_to, close_path + fill_winding() / stroke() — path is implicit sequence, style is separate state (SetFillColor, SetStrokeWidth etc.) Dart device is aggregated (path+style in one call), nigig is split (state + path ops + paint). Port requires either adapting test to nigig's split API or adding aggregated path() method to PdfDevice (done for dense CAD via path.rs PdfPath)
Gradient/Mesh fillPathGradient(PdfPath, PdfFillRule, PdfGradient, alpha) , fillMesh(PdfMesh, alpha) — shadings and meshes as first-class calls PaintShading(String) + PendingShadings — shadings are pending names, meshes not yet exposed Mesh/gradient device calls not yet ported; currently via Display only
Text showText(PdfTextRun) with PdfTextRun carrying PdfPath glyph outlines, advances, etc. ShowTextWithMetrics { text, font_size, advance_x, glyphs: Vec<GlyphPlacement> } Close but PdfTextRun in dart has richer PdfPath per glyph for embedded fonts
State save()/restore() + setFillColor etc. but fillPath takes color as param, not state State is GraphicsState stack, color set via SetFillColor then FillWinding Dart's fillPath(path, color, ...) bundles color, nigig's splits it

Verdict: Interpreter parsing is ~90% 1:1 (all operators covered, plus Type 3, shading sh, streaming). Interpreter device API is ~60% 1:1 — the path+style aggregation is the missing bridge that path.rs Phase 7.5 just built.

2. Scope (Phase 2.5, ~1-2 sessions, no fork)

2.1 Add aggregated device methods to PdfDevice (back-compat, default no-op)

fn fill_path(&mut self, path: PdfPath, color: [f64;4], rule: PdfFillRule, alpha: f64) { self.path(path); self.fill_winding() } // default impl via existing split API
fn stroke_path(&mut self, path: PdfPath, color: [f64;4], stroke: PdfStroke, alpha: f64) { ... }
fn clip_path(&mut self, path: PdfPath, rule: PdfFillRule) { ... }
fn fill_gradient(&mut self, path: PdfPath, gradient: PdfGradient, rule: PdfFillRule, alpha: f64) {}
fn fill_mesh(&mut self, mesh: PdfMesh, alpha: f64) {}

This lets the dart test's RecordingDevice be ported verbatim: it implements PdfDevice and records fill_path calls with PdfPath objects.

2.2 Port interpreter_test.dart (1885 LOC) → crates/apps/pdf/pdf-graphics/tests/interpreter.rs

Split into groups as dart does:

  • device familiesPdfColorSpace channel counts (already in colorspace.rs but re-assert via interpreter)
  • path opsm, l, c, v/y, h, re, h clipping, winding/even-odd
  • textBT/ET, Tf, Td/Tm, T*, Tj/TJ with glyph placements
  • graphics stateq/Q, cm, w/J/j/M/d, gs with ca/CA/BM
  • shadingsh via PaintShading pending
  • XObjectDo for image/form (form already expanded, image pending)
  • marked contentBMC/BDC/EMC with MCID
  • Type 3d0/d1 metrics

Each group becomes a Rust #[test] that builds PdfOp vec via parse_content_stream + RecordingDevice::from_ops and asserts on calls, fills, strokes, clips as dart does, but using PdfPath + PdfColor + PdfStroke.

2.3 Port streaming_interpreter_test.darttests/streaming_interpreter.rs

Verifies ContentStreamIter + interpret_streaming incremental path: same ops as above but fed via streaming_interpreter that yields PdfOp lazily, and that state survives across chunks.

3. Test Port Table (first tranche)

# dart test Rust test Notes
T1 device families (color space names) interpreter::tests::device_families Already in colorspace.rs — re-use
T2 path ops group path_ops_fill_and_stroke_with_path Use PdfPathBuilder to build expected path, fill_path to assert
T3 text group text_with_glyph_placements ShowTextWithMetrics glyphs already have offsets
T4 graphics state save/restore save_restore_preserves_path_and_color q/Q with fill_path inside
T5 sh shading sh_produces_pending_shading PaintShading pending
T6 BMC/BDC/EMC marked_content_carries_mcid BeginMarkedContent with MCID

Full 1885-line file is split across 2 sessions; T1-T6 are session 1 (~6 tests), remainder session 2.

4. Exit Criteria

  • 6+ new tests green: cargo test -p nigig-pdf-graphics --test interpreter --offline
  • cargo test -p nigig-pdf-graphics --lib --offline still 435 green (no regression)
  • NIGIG_PDF_FEATURE_PARITY_PLAN.md updated: interpreter_test / streaming_interpreter_test | DONE | nigig interpreter.rs — aggregated PdfPath device + streaming
  • ADR 00xx-pdf-interpreter-aggregated-device.md records why PdfDevice now has both split (move_to) and aggregated (fill_path) APIs and that the aggregated one delegates to split by default.

5. Risks

  • API duplication — mitigation: aggregated fill_path default impl calls split API, so existing devices (including MakepadPdfDevice) need no change unless they want the aggregated path.
  • Heavy test file — mitigation: port in groups, not monolith; first tranche 6 tests is the proof.