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

77 lines
6 KiB
Markdown

# Plan: 1:1 Port of dart-pdf `interpreter_test.dart` → `nigig-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)
```rust
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 families``PdfColorSpace` channel counts (already in `colorspace.rs` but re-assert via interpreter)
* `path ops``m`, `l`, `c`, `v`/`y`, `h`, `re`, `h` clipping, winding/even-odd
* `text``BT`/`ET`, `Tf`, `Td`/`Tm`, `T*`, `Tj`/`TJ` with glyph placements
* `graphics state``q`/`Q`, `cm`, `w`/`J`/`j`/`M`/`d`, `gs` with `ca`/`CA`/`BM`
* `shading``sh` via `PaintShading` pending
* `XObject``Do` for image/form (form already expanded, image pending)
* `marked content``BMC`/`BDC`/`EMC` with `MCID`
* `Type 3``d0`/`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.dart` → `tests/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.