9.2 KiB
Engine Roadmap — Reality Audit & Gap Analysis
Status: Review-only. No code changed to produce this. Question answered: Of the roadmap items marked Done, which are real and which are mocks/stubs? And do the stubs belong to a later phase?
How this was verified (not taken on faith)
- Read the C++ engine dispatch in pdfium_document.cpp — specifically
applyEdits(line 1432) and every edit-type handler. - Runtime-tested each edit op through the real engine bindings (gateway venv): apply →
save→ reload → assert the change actually persisted (annotation count, text length, page count). - Read the gateway edit pipeline edits.py (incl. the image preprocessing).
- Confirmed deps + artifacts: Pillow 10.4.0 present (needed for images),
pdfengine.wasm(60 KB) built, enginepdfium=on, skia=off.
1. Headline findings (the important part)
| # | Finding | Impact |
|---|---|---|
| F1 | freehand (ink) is an engine STUB. Handler at line 1867 is literally spdlog::info("Parsed freehand edit operation (stub)") — it parses and does nothing. |
Roadmap says "Freehand / Ink Annotations — Done 100%" — that is false end-to-end. Ink never persists to the saved PDF. |
| F2 | free_text is an engine STUB. Handler at line 1804 is spdlog::info("Parsed free_text edit operation (stub)"). |
The Text-box tool (which sends free_text) won't persist. (Note: text_overlay is real — see F3.) |
| F3 | Several items marked "Done — 0%" (contradictory) are in fact REAL and working: highlight, comment, Edit-Layer schema. The 0% column is just wrong. |
Good news — these are done. |
| F4 | redaction (roadmap: In Progress) is functionally complete — it removes overlapping page objects, covers, and regenerates content (line 1566). Verified: page text dropped 312→101 chars. |
Under-reported; it's effectively done. |
| F5 | Frontend "text selection + copy — Done" was a mock (SelectionLayer returned fake text) until it was wired to real glyph data this session. |
Now real; flagging the roadmap was wrong. |
2. Edit operations — verified truth table
Tested by applying each op to a real corpus PDF and reloading the result.
| Edit op | Roadmap | Engine handler | Runtime result | Verdict |
|---|---|---|---|---|
highlight |
Done (0%) | Real (FPDF_ANNOT_HIGHLIGHT, quadpoints, color, author) |
annots 0→1 | ✅ REAL |
comment |
Done (0%) | Real (FPDF_ANNOT_TEXT sticky) |
annots 0→1 | ✅ REAL |
text_overlay |
Done (100%) | Real (draws text objects, font mapping) | text 312→321 | ✅ REAL |
image_overlay |
Done (100%) | Real engine + gateway converts data-URI→BGRA→temp file | needs gateway path (Pillow ✓) | ✅ REAL (via gateway) |
redaction |
In Progress | Real content removal + cover + regen | text 312→101 | ✅ REAL |
page_rotation |
Done (100%) | Real (FPDFPage_SetRotation) |
saved, bytes changed | ✅ REAL |
page_deletion |
Done (100%) | Real (FPDFPage_Delete, guards last page) |
pages 100→99 | ✅ REAL |
page_reorder |
Done (100%) | Real (FPDF_MovePages) |
saved ok | ✅ REAL |
free_text |
(in schema) | STUB — no-op | no change | ❌ STUB |
freehand (ink) |
Done (100%) | STUB — no-op | no change | ❌ STUB |
8 of 10 edit ops are genuinely real and verified. 2 (freehand, free_text) are stubs.
3. Why the stubs matter right now (frontend consequence)
Both stubs are wired into the live UI, so they look like they work but silently fail on save:
- Draw / Ink tool → emits
freehand→ engine ignores it. In live (non-mock) mode the stroke renders locally, then disappears after the save round-trip (the reloaded annotation list fromextract_annotationswon't contain it). It will not be in the exported PDF. - Text-box tool → emits
free_text→ same: text vanishes after reload; not in export.
These didn't show up earlier because we've been in mock mode (engine flag off), where edits don't actually round-trip. They'll surface the moment the engine is enabled.
Cheap mitigations (for later, your call):
- Text-box: re-point it from
free_text→text_overlay(already real). ~1-line change in the frontend handler. - Ink: needs a real engine
freehandhandler (write a PDF Ink annotation,FPDF_ANNOT_INK, from thepaths) — a small, well-scoped C++ addition, not a future-phase feature.
4. Roadmap status, corrected
Phase 0 — Infra (8 items, all Done)
✅ All real and consistent. CMake/vcpkg/CI, PDFium/Skia/FreeType/HarfBuzz builds, FastAPI + React scaffold, WASM hello-world, frozen contracts. No issues.
Phase 1 — Core engine + viewer (13 items, all Done)
Mostly real. Corrections:
- "React: text selection + copy — Done" → was a mock in the live viewer; wired to real glyphs this session. Engine-side glyph extraction was real all along.
render,text extraction w/ bounds, DPI/coordinate transforms, font load/substitution, thumbnails, incremental save — verified real.- "C++ Engine → WASM Facade — Done" →
pdfengine.wasmis built (60 KB) andwasmLoaderloads it, but falls back to a JS mock on failure, and the live app renders server-side (/render), not via WASM. So the facade exists; it isn't the primary render path.
Phase 2 — Editing (status mixed)
- Real & done:
text_overlay,highlight,image_overlay,comment,page_rotation,page_deletion,page_reorder, Edit-Layer schema. (Several mislabeled 0%.) - Marked Done but STUB:
freehand/ink (F1). ← the one genuinely wrong "Done". - Schema-only / STUB:
free_text(F2). - In Progress but effectively real:
redaction. - Genuinely In Progress / Not Started (correctly labeled):
- Annotation reader (read exists via
extract_annotations; "write/edit existing" not there). - React annotation toolbar/editing UI (built this session, but edit/delete of existing annots has no engine op).
- Form field viewing — Not Started (no engine API).
- Export full-save — engine
save_fullworks; gateway endpoint exists; treat as functionally real. - WASM render path + Web Worker — In Progress / Not Started (secondary).
- Annotation reader (read exists via
Phase 3 — Advanced (16 items, all Not Started) ✅ accurate
Content-stream analyzer, edit existing text / replace / reflow, underline/decoration, font embedding for edits, custom path/Skia rasterizer, glyph-accurate hit-testing, form filling, encryption/password, regression suite, fuzzing, native SDK packages. None started — correctly labeled. This is where "true PDF editing" (retype existing text), forms, and password PDFs live.
Phase 4 — Perf/enterprise (gated, Not Started) ✅ accurate
Custom glyph pipeline, tiling, GPU, reflow, accessibility, digital (cryptographic) signatures, advanced forms. Out of v1 scope, correctly labeled.
5. Are the mocks/stubs "part of upcoming phases"?
Two different cases — important distinction:
| Stub | Belongs to a future phase? | Reality |
|---|---|---|
freehand (ink) |
No. It's a Phase 2 item marked done. The real impl (write an Ink annotation) is small and overdue, not future work. | Incomplete Phase-2 task mislabeled "Done". |
free_text |
Partly. A simple FreeText annotation is small Phase-2-grade work. True editable/reflowing text is Phase 3 (Text replacement engine, Not Started). |
The basic annotation is a quick win; full text editing is correctly future. |
| Edit existing annotations (delete/update) | Not on any phase. No edit op exists. | Net-new small backend ticket. |
| Outline/bookmarks, forms, password PDFs, edit-existing-text | Yes — Phase 2/3, Not Started. | Correctly future; the UI already stubs these as "coming soon". |
6. Bottom line
- Engine completion is genuinely high for v1-style editing: 8/10 edit ops real and verified, render/text/search/pages/redaction/export all real.
- The roadmap over-claims exactly two things:
freehand/ink is "Done" but a stub, and the "Done 0%" rows are mislabeled (they're actually real). It also under-claimsredaction. - The only end-to-end-broken user-facing features are Ink and Text-box (both ride the two stubs). Both have cheap fixes; neither requires Phase 3.
- Everything labeled Phase 3/4 "Not Started" is accurate — that's the real remaining work (edit existing text, forms, encryption, perf, certified signatures).
7. Suggested next steps (for review — nothing changed yet)
- Correct the roadmap:
freehand= In Progress (stub);highlight/comment/schema = Done (fix the 0%);redaction= Done. - Decide the two cheap fixes: (a) re-point Text-box tool to
text_overlaynow; (b) implement the enginefreehand→ Ink-annotation handler. - Add two small backend tickets already flagged by the UI:
delete/update_annotationop, and an outline endpoint. - Leave Phase 3/4 as-is — correctly scoped future work.