Merge upstream/main into feat/recent-reveal-5452

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
vangel76andClaude Sonnet 5.5 committed 2026-11-11 22:24:02 +01:00
commit 08bf6ec278
207 files changed
+18364 -1776

No files matched your search

+175
View File
@@ -0,0 +1,175 @@
# AcroForm script workflows
This contribution connects the existing AcroForm object model to the native viewer and the
shared automation tools. The regression fixture is generated in Rust; it contains no private
document, employee, signature image or real PIN.
## Runtime and lifecycle
Each open document owns a `ScriptSession`. On native targets its Boa context lives on one
worker thread with a dedicated 64 MiB stack reservation (the transient stateless runner retains
its separate stack policy). Document scripts initialize once with `Doc/Open`, before the requested event;
button, console, format, validate and calculate scripts then share that context. Field objects
read the latest snapshot supplied by the engine, including objects retained by earlier scripts.
Exposed Field objects are cached by PDF object identity; synthetic fields use name keys. Separate documents cannot share globals or
icon objects. `eval` and dynamic Function construction remain disabled.
Changing the document-script sources recreates the context. Closing, reopening, reloading,
Revert, disabling JavaScript, and saving a full rewrite release/reset the context. Incremental saves retain it. A rewrite can reuse PDF object numbers, so old Field and icon objects must not be carried across it. PDF Undo/Redo restores PDF state;
it does not serialize or rewind arbitrary JavaScript variables. Globals are not saved into the
PDF. A worker fault resets its context; the existing cooperative source/loop/recursion bounds
remain in force. The source preflight additionally caps active arrow chains at 256 and right-associative
assignment/exponentiation chains at 4,096 across open bracket levels. Independent statements
and comma-separated sibling expressions reset only their own level; comparisons do not count.
Keyword unary/new chains share the prefix
depth bound of 64. These conservative limits protect recursive parser/compiler paths before
execution on the smaller worker stack. Boundary and refusal cases run in subprocess regressions.
These bounds do not establish a hard JavaScript heap quota.
`calculateNow()` evaluates the current calculation list synchronously, in `/CO` order, using
pending writes. Later statements in the same invocation see the new values. The caller's
event is restored after calculation, including failure. Reentrancy is capped at eight levels.
Field writes and their dependent recalculation use the existing single undo transaction.
Pure caption, geometry, style and visibility updates do not run the calculation chain.
Page geometry is read once per edit runner. Calculation sources/order are analyzed once on its
first callback and reused; field values and appearance properties still refresh on every callback.
Structural/document changes between batch children refresh the snapshot, and editing a field's
calculation script or calculation order invalidates the cached plan before recalculating.
`numPages`, `documentFileName` and `info` read the current event snapshot, including page/info edits,
Undo/Redo and completed Save As, without reinitializing script globals.
## Fields, appearances and icons
- `buttonGetCaption()` reads the first widget. An unindexed `buttonSetCaption()` updates every
child widget, including when the first already has the requested text. An unchanged uniform
caption does not create an edit. Only normal face `0` is implemented.
- `rect` reads/writes rotated user-space `[left, top, right, bottom]`. Writes affect widget 0,
as specified for an unindexed Field. Conversion uses its page rotation and effective Crop
box. Small/zero-area rectangles remain representable, rather than applying the interactive
field-authoring minimum size to a script's overlay.
- `textColor`, `fillColor`, `strokeColor`, `textSize`, `lineWidth`, `borderStyle`, `userName`,
`readonly` and `display` flow to the form model and saved appearances. Colour input supports
`T`, `G`, `RGB` and `CMYK`; text cannot be transparent. The existing appearance editor writes
its canonical RGB representation. Partial changes preserve unrelated fonts and properties.
- `buttonGetIcon()` returns a host-minted object for a normal Form XObject icon. `buttonSetIcon`
accepts only a handle minted in the same context, or null to clear it. It reuses the form and
its resources, including transparency, instead of rasterizing a signature/background. The
existing button appearance generator draws it. Other icon faces are refused.
- `getPageBox` reads effective/inherited page boxes in rotated user space. `zoom`,
`app.runtimeHighlight` and `setFocus()` communicate with the native viewer. Automation
reports viewer requests and does not open an external application or change a real viewer.
## Native menus and input
`app.popUpMenuEx` supports nested menus, separators, marked/disabled items, explicit return
values and cancellation. `app.response` supports named and positional parameters, defaults,
password masking and null cancellation. Alert replies use the documented numeric button ids.
Only the worker waits for a reply; egui continues drawing/input. The central modal gate blocks
document shortcuts and page input while a prompt is visible. Idle prompt polling uses a
one-second repaint interval; replying wakes the UI immediately and worker polling remains 16 ms. A reply resumes the same
invocation, without replaying the script or its side effects.
An explicit button captures document identity, generation and view geometry. Closing/switching
the document, editing it, disabling scripts or changing the relevant view discards a stale
completion and invalidates the old runtime. A reply is checked against an enabled menu leaf
and its enabled ancestors; invented/disabled return values fail with a script error. Prompts
have at most 1,024 entries, 16 submenu levels, 4,096-byte strings and 32 calls per invocation.
The native desktop admits one pending button interaction. A document worker refuses a second
run while the first is active, including while waiting for a prompt; it never queues a
synchronous field callback behind an unanswered dialog.
The native prompt UI is not implemented for the web build. The existing headless/unsupported
host path cancels unanswered popups and response dialogs; no web-interaction parity is claimed.
This work does not add timer execution, PDF annotations/layers to the JS object model, or the
remaining document action triggers.
## Automation
The existing `js_run` accepts `answers`, an optional array of up to 32 strings or null values:
```json
{
"doc": 1,
"script": "var t=app.popUpMenuEx({cName:'Manual',cReturn:'manual'}); if(t==='manual') getField('time').value=app.response({cQuestion:'Time'});",
"answers": ["manual", "07:45"]
}
```
The schema advertises a 4,096-character maximum per answer; the handler additionally enforces
a 4,096-byte UTF-8 budget, so multi-byte strings can reach the byte limit earlier.
Replies are consumed in prompt order. Null/missing replies cancel a menu/response; alert
defaults choose acknowledgement, cancellation or No according to the button set. No direct
file, process or network API is exposed by a prompt. Root confinement and permission-checked
document edits continue through the existing automation/engine boundary.
## Related contributions
The persistent runtime uses the upstream work from #5304 for synchronous calculations,
#5501 for page boxes, #5499 for colour parsing and writeback, and #5505 for normal captions.
The integration supplies the live document state, exact field identity, and grouped field
selection needed by the persistent context.
The bounded `popUpMenuEx` parser in `crates/js/src/interaction.rs` was adapted from #2081.
The prompt channel and default menu renderer support interactive form scripts. #2081 can
supply an anchored renderer through the UI menu callback when it adds link actions. Once
the shared parser lands upstream, this integration can use it directly.
`ScriptSession::run_host_task` gives future timer and page actions (#4751) access to the
retained realm on its own worker.
Document actions and widget triggers (ported from #344 by @DWAA1660) run through the same
realm. On opening, the document scripts run, then the catalog `/OpenAction`'s JavaScript
and its `/Next` chain (event `Doc/Open`); Revert runs them again in a fresh realm.
`Session::run_document_actions` runs `/AA /WS` before a save and `/AA /WP` before printing:
the app's Save and Print and the `doc_save` and `doc_print` tools call it first. A failing
action is reported and never stops the save or print. `Session::field_trigger` runs a
widget's Mouse Enter, Mouse Exit, Mouse Down, Mouse Up, Focus or Blur JavaScript; the app
queues them from the pointer and the focus, and `js_event` runs them headlessly. A check
box's or radio button's Mouse Up runs after its click has stored the new value (#247).
Scripts that run unasked (open, Will Save, Will Print, pointer over, focus) may only ask for
highlighting, zoom, focus or a page.
The lexical preflight recognizes unambiguous top-level and block-body ASI boundaries before identifiers, including declarations and assignments separated by line breaks or comments. It preserves counters after operators and member access, and across binary keyword operators. It distinguishes control-statement parentheses for regexp literals; inside expression parentheses, array/object literals and template interpolations it remains conservative; this is not a complete ECMAScript parser or ASI implementation.
Native persistent workers are capped process-wide at four on 32-bit targets and sixteen on 64-bit targets. Each reserves 64 MiB of stack; closing a document releases its slot after the worker exits. Capacity errors ask the caller to close a scripted document and retry; other documents are never silently evicted. The transient stateless runner has its separate existing stack policy.
Synchronous runs have one ten-second host deadline covering startup and every prompt round trip. A timeout replaces the document realm; late globals and custom Field properties cannot reach later runs. This is isolation, not forcible termination of an OS thread: cooperative engine limits still govern the detached worker, whose capacity slot stays charged until it exits. No hard CPU or heap quota is claimed.
An asynchronous completion stays reserved until the engine accepts it. An intervening edit forks a fresh realm before any field calculation, including when the edit later fails. Pending results carry runtime identity as well as generation, so discarding an old result never clears a newer realm. Script prompts render last and explicitly raise their retained egui layer, including when another app dialog opens later. Null prompt answers cancel popup/response input; alert answers instead select the button set's default (OK, Cancel or No).
`resetForm()` updates the live field shadow synchronously, including retained Field references and named parent groups. Reset requests still reach the PDF engine for default-value and appearance semantics; the atomic writeback performs resets before applying the final script delta. Delta comparison uses the reset baseline, so a later script assignment restoring the original pre-reset value is preserved. Doc Open and the first button/console event share this ordering, including after JavaScript is disabled and re-enabled. PDF Undo/Redo and save/reopen preserve the final values.
The reset comparison baseline is obtained by running the existing forms reset path on an isolated document copy. It therefore includes unnamed built-in Calculate text/combo dependents, without executing custom Calculate scripts during the preview. Normal automatic calculation after script value writes remains in force; this does not establish a policy that explicit formula-field writes override their formulas. Tests cover saved defaults left untouched by the event and a non-default typed native response as well as existing default/cancellation paths.
Reset writeback is admitted before request copies, preview cloning and PDF mutation only when it fits 64 reset calls, 4,096 aggregate selectors, and 2^24 estimated work units. Named resets pay for the bounded selector scan and the fields actually matched. The estimate charges both preview/commit passes, field/calculation/dependency scans, calculation source lengths and selector scans/bytes. Excess work returns an explicit error without committing PDF edits or an undo entry. Under-budget requests retain their order; duplicate resets are not silently merged because repeated recalculation can be observable. This is an operation/amplification bound, not a hard UI wall-clock deadline. Native start/completion tests cover the 64-call boundary, 65/10,000-call refusal, selector overflow, aggregate large-form refusal, reset/restore, Undo/Redo and save/reopen.
The reset admission estimate also charges field-name copies, selector comparison bytes, and the worst-case expansion of each named reset to all fields (including both validation and membership scans). This conservative bound can refuse a named reset even if its eventual selection would be small; full-form resets avoid that named-selection cost. Refusal tests compare saved bytes as well as generation and undo state.
Reset admission includes the total widget count for preview and commit. The shared JavaScript string-array converter also enforces a 32 MiB aggregate escaped UTF-8 text budget before copying beyond it (including scalar input), independently of later request clipping. Exact Unicode and unpaired-surrogate output bytes are charged; the existing array-item limit remains. JavaScript ToString/getters may still execute script code before conversion; this is not a general JavaScript heap limit.
Unknown PDF defaults (failed stream decoding or a spent shared value budget) are marked in the JavaScript snapshot. A reset preserves their live values, matching the forms reset policy; explicit assignments before/after reset remain effective. The policy follows inherited defaults too. Menu and response properties now use the bounded escaped UTF-8 conversion at 4096 bytes per property before copying past that limit; user conversion exceptions are preserved. Existing item/depth/prompt/reply limits remain separate. Immediate dependent reads can use resetForm(...); calculateNow(); the reset callback timing itself has not been changed without an Acrobat parity oracle.
The 64-reset call budget is now enforced in the worker before argument conversion and field scanning, shared by document initialization and its first event, and reset for later jobs. A sticky outcome flag rejects PDF writeback even if JavaScript catches the exception. The existing engine admission/work guard remains. Calculations are resolved by PDF object reference to the matching snapshot position; event targets and duplicate-name value and property deltas retain object identity through the host, authoring helpers and redraw. Public getField(name) retains first-match behavior. Tests cover both calculation orders, target reads/writes, native/console completion, Undo/Redo and save/reopen.
AFSimple_Calculate expands exact/group/duplicate field matches without materializing their cross-product; numeric conversion and empty-result fallbacks remain unchanged. Selector comparison work is admitted up to 2^24 estimated units. The Field cache uses canonical PDF identities (name-only keys for synthetic callers), prunes removed fields between jobs and covers every exposed field, including more than 4096; renames preserve retained references/custom properties. Script color changes detach outside widgets that share the target group's indirect MK, including bounded reference aliases, while keeping target widgets' indirect object and unrelated MK entries. Native button clicks carry exact identities through hit testing, action dispatch and event construction. Legacy name-only engine/UI entry points remain wrappers. Headless form_fields returns field_id; js_run accepts that optional 48-bit identity with a matching field name, validates it before execution, and retains deterministic prompt replies. IDs are document-local and must be rediscovered after structural changes.
Field instance tokens live outside PDF undo snapshots. Successful edits and undo/redo retire
removed field instances immediately, even when no script job observes the gap. Reusing the
same PDF object number does not revive an old Field accessor; document globals survive history.
A refused synchronous/native writeback invalidates its document realm before returning an error.
The host-backed event accessor remains usable when globalThis is frozen. AFSimple_Calculate
shares a 2^24 selector-work budget across all calculation sources in an event job; later jobs
receive a fresh budget. Native simple calculations include every selected list export value,
and calculateNow uses the regular value setter's checkbox/radio normalization.
Native UI feedback distinguishes a refused busy click, a stale interaction and a timeout.
Timeouts report discarded results without claiming worker termination. Visible comparison halves
may complete; switching away, changing the view or editing still invalidates stale interactions.
Focus reveals a laid-out widget's page, and image import carries exact clicked identity through
the picker and SetFieldImageTarget. The headless form_set_image tool accepts the same optional
field_id and validates exact name/identity pairs; omitted IDs preserve legacy lookup. Mixed widget icons emit field-wide changes only when an
icon setter runs. UI waiting helpers use elapsed deadlines rather than machine-speed retry counts.
Comparison script document requests use their source view. Save As captures the source document without changing the comparison tab. Print activates and retains the source tab because the existing print dialog reads the active document throughout its lifetime. Missing source views never fall back to another active document. Regression coverage distinguishes a two-page source from a one-page comparison file and checks saved bytes, print-dialog page state and untouched comparison content.
Explicit calculation logs ordinary field script errors and continues in calculation order. Each job can evaluate at most 4,096 calculation scripts, including nested calls and document initialization on the first event. Exhaustion remains in effect after a JavaScript catch and prevents PDF writeback.
+4 -3
View File
@@ -1,6 +1,6 @@
# File format parity
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-19 · **Change:** patch (`.p7b`/`.p7c` bundles read for trust import) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-22 · **Change:** minor (Word, Excel, PowerPoint, OpenDocument and RTF open through an installed LibreOffice; `.p7b`/`.p7c` bundles read for trust import) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
Every format Acrobat Pro reads or writes, and PdfCraft's support for it. Acrobat's list comes from
its bundle's `Info.plist` `CFBundleDocumentTypes` (what it opens) and its public documentation
@@ -68,9 +68,9 @@ features total).
| Plain text | yes | yes | good | `create.from-text` |
| Clipboard (image or text) | yes | yes | good | `create.from-clipboard` |
| Screenshot | yes | no | none | `create.from-screenshot` planned |
| Word, Excel, PowerPoint (and Visio, Publisher on Windows) | yes | no | none | `create.from-office` planned (LibreOffice sidecar) |
| Word, Excel, PowerPoint (and Visio, Publisher on Windows) | yes | yes, desktop only, through an installed LibreOffice (File ▸ Open); not Visio or Publisher | good where LibreOffice is (its layout, not measured against Acrobat) | `create.from-office` partial |
| HTML file, web page URL | yes | no | none | `create.from-html`, `create.from-web-page` planned |
| RTF | yes | no | none | |
| RTF | yes | yes, through LibreOffice (as Office documents) | good where LibreOffice is | `create.from-office` partial |
| PostScript / EPS (Distiller) | yes | no | none | `create.distiller` planned (P3) |
| Markdown | no | no | none | `create.from-markdown` planned (P2; not an Acrobat feature) |
| Scanner | yes | no | none | `ocr.scanner-acquire` planned |
@@ -110,6 +110,7 @@ features total).
| Date | Change | Summary |
|---|---|---|
| 2026-10-22 | minor | Office documents (Word, Excel, PowerPoint, OpenDocument, RTF) open from File ▸ Open as new PDFs, converted by an installed LibreOffice; `create.from-office` partial. |
| 2026-10-19 | patch | Certificates row: `.p7b`/`.p7c` bundles (DER or PEM `PKCS7`/`CMS`) are read for trust import. |
| 2026-10-12 | minor | XDP split into data (read and write for XFA forms, already shipped under `form.xfa-data`) and opening `.xdp` as a document (none). Create PDF reads BigTIFF; malformed GIF headers are no longer detected as images. |
| 2026-10-11 | minor | #773: inferred tables have no invented borders; rotated or overlapping invoice pages use floating shapes and page sections. Full Office layout fidelity remains partial. |
+20 -7
View File
@@ -1,5 +1,6 @@
# Where PdfCraft falls short of Acrobat Pro
> **Last reviewed:** 2026-11-11 | **Last updated:** 2026-11-11 | **Change:** minor (gap 8: known residual risk for the LibreOffice sidecar, sandbox tracked in #6953; estimates unchanged)
> **Last reviewed:** 2026-10-28 | **Last updated:** 2026-10-28 | **Change:** minor (M10, #4119 partial: Enhance scanned file cleans scanned pages in place: background removal, edge shadow removal, despeckle; estimates unchanged)
> **Last reviewed:** 2026-10-25 · **Last updated:** 2026-10-25 · **Change:** minor (gaps 5 and 14 current state: native release Hans face via LXGW WenKai Lite; Arabic logical catalog, explicit RTL direction and mirrored stock chrome, with native/browser/IME workflows unqualified; no readiness re-estimate)
> **Last reviewed:** 2026-10-15 · **Last updated:** 2026-10-15 · **Change:** minor (gap 3: #2517 a blank line stays a paragraph break; estimates unchanged) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
@@ -45,7 +46,7 @@ update the numbers.
| 5 | Chinese UI lacks a web glyph face | localization | 6–12 | [localization-parity.md](localization-parity.md) |
| 6 | Open-issue backlog of wrong results in shipped features | stability | 45–80 | this file |
| 7 | OCR reads only unaccented Latin | feature | 40–75 | [file-format-parity.md](file-format-parity.md) |
| 8 | Office export weak, Office import absent | file format | 55–100 | [file-format-parity.md](file-format-parity.md) |
| 8 | Office export weak, Office import basic | file format | 55–100 | [file-format-parity.md](file-format-parity.md) |
| 9 | No Preflight; PDF/A partial; PDF/X and PDF/UA absent | standards | 65–115 | [pdf-spec-parity.md](pdf-spec-parity.md) |
| 10 | Signatures: no PKCS #11, timestamp servers or online revocation | feature, hardware | 40–70 | [pdf-spec-parity.md](pdf-spec-parity.md) |
| 11 | Accessibility: checker only, no tagging tools | feature | 40–70 | [target-app-parity.md](target-app-parity.md) |
@@ -189,19 +190,29 @@ update the numbers.
- **Estimate:** 40–75 h, partly blocked on openly licensed models. **Doc:**
[file-format-parity.md](file-format-parity.md).
### 8. Office export weak, Office import absent
### 8. Office export weak, Office import basic
- **Missing:** Word export that keeps layout; Excel and PowerPoint export; creating PDFs from Office,
HTML, web pages and PostScript; SVG/XML/EPS/PS/JPEG 2000 export.
- **Missing:** Word export that keeps layout; Excel and PowerPoint export; creating PDFs from HTML,
web pages and PostScript; SVG/XML/EPS/PS/JPEG 2000 export. Office documents open from File ▸ Open
through an installed LibreOffice, but Insert pages, Combine files and Create ▸ Multiple files
don't take them yet.
- **Evidence:** `create.export-docx` partial; the synthetic invoice regression for
[#773](https://github.com/storytold/pdfcraft/issues/773) now covers absent inferred borders,
floating watermark text and per-page section sizes. Exact font matching, vector rules and
broader layout fidelity remain incomplete;
`create.export-xlsx`, `create.export-pptx`, `create.from-office`, `create.from-html` planned.
`create.from-office` partial (LibreOffice sidecar; Writer and Calc never update linked content,
but Impress has no such setting and the process's network access isn't sandboxed);
`create.export-xlsx`, `create.export-pptx`,
`create.from-html` planned.
- **Known residual risk:** conversion runs the user's installed LibreOffice. PdfCraft
opens no connection itself. Macros are disabled, untrusted referer links are blocked,
and Writer and Calc never update links. Impress has no link-update setting, and the
`soffice` process isn't network-sandboxed, so linked content in presentations could be
fetched. Sandboxing the converter is [#6953](https://github.com/storytold/pdfcraft/issues/6953).
- **Impact:** "Export PDF" and "Create PDF" are two of Acrobat's most used tools.
- **Gate:** lossy or missing sub-cases of the convert-and-share workflow (partial, not blocking).
First beta items: Create PDF from Office (LibreOffice sidecar, 10–20 h) and a Word export that
keeps layout (20–35 h).
First beta items: the rest of Create PDF from Office (Insert, Combine and Create ▸ Multiple files
on a worker, a Create ▸ From Office command; 5–10 h) and a Word export that keeps layout (20–35 h).
- **Estimate:** 55–100 h. **Doc:** [file-format-parity.md](file-format-parity.md).
### 9. No Preflight; PDF/A partial; PDF/X and PDF/UA absent
@@ -422,6 +433,8 @@ update the numbers.
| Date | Change | Summary |
|---|---|---|
| 2026-11-11 | minor | Gap 8: known residual risk for the LibreOffice sidecar (Impress has no link-update setting; the soffice process isn't network-sandboxed). Sandboxing tracked in #6953. Estimates unchanged. |
| 2026-10-26 | minor | Gap 8: the LibreOffice profile never updates Writer or Calc linked content on load; Impress has no such setting and the process's network access isn't sandboxed. Estimates unchanged. |
| 2026-10-25 | minor | Gaps 5 and 14 current state: native release builds embed LXGW WenKai Lite for Simplified Chinese, while the web build has no Hans face; Arabic catalogs stay logical with explicit RTL label direction and mirrored stock chrome, while native/browser RTL input, IME and complex workflows remain unqualified. Estimates unchanged. |
| 2026-10-18 | minor | Gap 3: #2517 a partial rewrite keeps a kept source row in the paragraph and leaves the next marked region closed around its text. Estimates unchanged. |
| 2026-10-15 | minor | Gap 3: #2517 a blank line at ordinary leading stays a paragraph break. Estimates unchanged. |
+19 -1
View File
@@ -1,3 +1,8 @@
- 2026-11-04 (M6, PR #5699 group/cache/MK/button review): Expand calculation groups/duplicates with streaming bounded lookup; retain one canonical Field object per exposed identity and prune stale roots. Protect outside shared-MK owners and carry exact button IDs through native clicks and js_run, with regression and persistence coverage.
- 2026-11-04 (M6, PR #5699 early reset/duplicate identities): Enforce the shared 64-call reset budget before argument conversion/scanning, with sticky atomic rejection and Open/event budget sharing. Preserve PDF object identity in Calculate order, live targets and value writeback; regress duplicate names, reversed order, native completion and persistence.
- 2026-11-04 (M6, PR #5699 independent review): Preserve unknown defaults through the JS reset shadow; bound menu/response text before copies. Cover native/console reset persistence, inherited defaults, decode-budget exhaustion and exact Unicode dialog boundaries.
- 2026-11-04 (M6, PR #5699 memory/widget bounds): Count reset widgets and bound escaped UTF-8 text conversion before copies; add negative and Unicode boundary regressions.
- 2026-11-04 (M6, PR #5699 reset estimate): Charge expanded-name scans and field-name copy/comparison bytes; add three negative regressions and byte-exact refusal checks.
# PdfCraft roadmap: milestones and what's next
> **Last reviewed:** 2026-10-27 | **Last updated:** 2026-10-27 | **Change:** minor (M10: vector printing preserves annotation optional-content membership; estimates unchanged)
@@ -12,19 +17,28 @@
> **Last reviewed:** 2026-11-09 | **Last updated:** 2026-11-09 | **Change:** patch (M10 #5130: preserve manual duplex defaults; validate DocumentDuplex review fix and CI formatting; estimates unchanged)
>
> **Last reviewed:** 2026-11-07 | **Last updated:** 2026-11-07 | **Change:** patch (M7 #2964 / #3185: grouped source text and Thai font/style preservation, with reported deep or unsafe groups and a per-glyph check of the Thai map repair; estimates unchanged)
> **2026-11-04 (M6, PR #5699 reset budget):** Reject reset amplification before request copies, preview and PDF mutation; cap reset/selector counts and aggregate estimated work. Add native completion, boundary, flood and large-form regressions; preserve ordered under-budget resets.
> **2026-11-04 (M6, PR #5699 dependent reset):** Reuse form reset on an isolated comparison document so unnamed built-in calculated text/combo fields are included; retain normal post-write calculation. Extend persisted-default and typed-response regressions.
> **2026-11-04 (M6, PR #5699 reset ordering):** Reset live field values synchronously, commit resets before final script deltas using a reset baseline, and cover Doc Open reinitialization, save/reopen, Undo/Redo and replay-sensitive UI counters.
> **2026-11-04 (M6, PR #5699 review):** Bound worker capacity and synchronous deadlines, isolate expired/pending realms before edits, keep script prompts above other dialogs, and cover block ASI, control-position regex literals, event routing and complete undo state. Validation is reported in the PR; parity remains partial.
> **2026-11-03 (M6, PR #5699 follow-up):** Accept independent top-level statements separated by automatic semicolon insertion while retaining continuation-chain limits; assert cyclic calculation results and preserve upstream multiline Fill & Sign coverage.
> **Last reviewed:** 2026-11-05 | **Last updated:** 2026-11-05 | **Change:** minor (M3, #5452: native Recent rows gain Reveal in Finder / File Explorer; estimates unchanged)
> **Last reviewed:** 2026-10-28 | **Last updated:** 2026-10-28 | **Change:** minor (M10, #4119 partial: Enhance scanned file cleans scanned pages in place: background removal, edge shadow removal, despeckle; estimates unchanged)
> **Last reviewed:** 2026-11-03 | **Last updated:** 2026-11-03 | **Change:** patch (M5 #5850 review: indirect typewriter intent and composer edits preserve outer whitespace; movement regression commits by outside click; estimates unchanged)
> **Last reviewed:** 2026-11-03 | **Last updated:** 2026-11-03 | **Change:** patch (M5 #5850: Enter adds Fill & Sign line breaks; outside clicks commit, Escape cancels, and saved multiline appearances retain their text and height; estimates unchanged)
> **2026-11-03, M7 / #5835 review:** active image crops retain secondary clicks over overlapping images; crop controls share measured layout with hit detection. Rotation, handle and page-edge regressions added; focused tests, scoped Clippy, formatting, layering and asset checks passed locally. Estimates unchanged.
> **Last reviewed:** 2026-10-31 | **Last updated:** 2026-10-31 | **Change:** minor (M3/M4, #5330: Crop pages is directly available in the document quick toolbar; estimates unchanged)
> **2026-11-02 (M6, PR #5699 second review):** Count active expression chains instead of whole-source tokens; align native button events and automation schemas, block document input under script prompts, and validate prompt dismissal and idle repaint scheduling.
> **Last reviewed:** 2026-11-01 | **Last updated:** 2026-11-01 | **Change:** patch (M7 #5657: header/footer, watermark and background operations commit all selected pages together; later-page errors preserve marks, content, shared/inherited resources and pending edits; estimates unchanged)
> **2026-11-02 (M6, PR #5699 review):** Cache per-edit script geometry/calculation plans, expose live document metadata, and bound persistent worker stacks to 64 MiB; add operation-count, lifecycle and isolated hostile-input/address-space regressions.
> **2026-11-02 (M6, #5211/#269):** Integrate persistent native AcroForm realms, synchronous calculation, page boxes, appearance/geometry/icon writeback and native button prompt continuation. Synthetic workflow and cancellation tests added; validation details accompany the PR. Remaining JavaScript parity stays partial.
> **Last reviewed:** 2026-11-01 | **Last updated:** 2026-11-01 | **Change:** patch (M7, PR #2155 further review: inherited drawing/text state, affine image gestures, paste/selection ownership, no-op scripts and menu permissions; estimates unchanged)
@@ -33,6 +47,8 @@
> **Last reviewed:** 2026-11-01 | **Last updated:** 2026-11-01 | **Change:** patch (M7, PR #2155 review follow-up: preserve image crop/tag/stream ownership, resize in page orientation, harden clipboard and text formatting, and reload installed fonts; estimates unchanged)
> **Last reviewed:** 2026-10-31 | **Last updated:** 2026-10-31 | **Change:** patch (M2, PR #4172: GBK rasters compared with explicit Unicode and independently selected installed faces; wrong-glyph and wrong-family negative controls fail; estimates unchanged)
> **Last reviewed:** 2026-10-31 | **Last updated:** 2026-10-31 | **Change:** patch (M2, PR #4172: integrate main fa5f9f8c; retain CJK viewer and upstream raster regressions, restore explicit display_config imports; estimates unchanged)
> **Last reviewed:** 2026-10-30 | **Last updated:** 2026-10-30 | **Change:** minor (M6: scripts can read and change a push-button's normal caption with undo/redo and save/reopen coverage; estimates unchanged)
> **Last reviewed:** 2026-10-28 · **Last updated:** 2026-10-28 · **Change:** patch (M2, PR #4172: integrate main 5af10276; retain both roadmap histories and viewer-only CJK font policy; estimates unchanged)
> **Last reviewed:** 2026-10-27 · **Last updated:** 2026-10-27 · **Change:** patch (M2, PR #4172: integrate main 1dd2737b; preserve viewer-only CJK fallback and regression coverage across engine, renderer and Combine module splits; estimates unchanged)
> **Last reviewed:** 2026-10-27 · **Last updated:** 2026-10-27 · **Change:** patch (M2, PR #4172: integrate main 9a9813bf and migrate viewer-only CJK configuration to the split engine modules; estimates unchanged)
@@ -273,7 +289,7 @@ total matches the parity estimate.
| M11 | Optimize, preflight, PDF/A/X/UA, print production | 200–350 | 18% | 80–140 | Done: new `optimize` crate: Reduce File Size and the PDF Optimizer (images measured where drawn, bicubic downsampling, JPEG/ZIP recompression only when smaller, discard objects and user data, Flate clean-up, resource merging, object streams). Target-version compatibility is partial: newer content is not detected or converted. Content stream boundaries preserve comments, split operands and graphics state when image fragments merge. Image downsampling includes page-local `/UserUnit` scaling when measuring physical resolution. Done since: reject invalid image and soft-mask dimensions with checked sizing (#2178). Missing: fonts and transparency panels, space audit, preflight, PDF/A/X/UA |
| M8 | Security + redaction | 100–180 | 68% | 15–30 | Done: opening protected documents, permissions, Protect Using Password, Remove security; redaction (mark text/areas/pages, Search & Redact with patterns, apply removing glyphs, image pixels, vectors, XObject content, annotations and fields, verification, full rewrite on save); Remove hidden information and Sanitize.<br>Done since: redaction codes (U.S. FOIA and Privacy Act sets as overlay text, in Redaction properties and `redact_mark`). Redaction cleans indirect and form structure references, preserving surviving form tags. Redaction retires inherited resource dictionaries, so a parent `/Pages` can't keep a removed XObject reachable; unredacted pages are left untouched. Image redaction removes alternate images and unsupported masks only on affected images. Changed marked-content spans lose replacement descriptions; untouched spans keep theirs. Hidden-text cleanup retires unused named descriptions across inherited/shared page and form resources (64 focused tests and scoped Rust 1.96 Clippy/wasm checks pass; publication CI pending). Retained text/image-bearing or unreadable tiling-pattern cells now make Apply refuse atomically after a bounded check of rewritten resources; fully removed cells still apply, and cross-page retention is checked. The redaction audit counts a filled cover that hides half of any one word of a long line (#2662), applied or not. Phone-pattern redaction also matches labelled Italian geographic numbers with `+39` and a slash after the area code (#3205 follow-up); unlabelled international slash numbers remain unsupported. Missing: certificate security, custom redaction code sets and pattern locales, DCT re-encoding |
| M9 | Signatures (PAdES, validation) | 160–280 | 60% | 40–70 | Done: new `sign` crate (DER, X.509, CMS, PKCS #12 on RustCrypto; aws-lc-rs for RSA private keys); PAdES B-B signing (visible, invisible, existing fields, certification with DocMDP); validation with trust store and changes-after-signing classification; self-signed digital IDs; Signatures panel, message bar, sign dialogs; agent tools. Checked with pdfsig and OpenSSL.<br>Done since: macOS Keychain and Windows Current User Personal store signing (software-backed CNG keys), certificate viewer, signed documents protected from rewrites, page validation overlays separate from original signature artwork, placed in the widget's bottom-right corner as it lies on the page, at a quarter of its shorter side and 48 screen points at most, so they annotate the artwork from a corner instead of covering the middle of it and turn with the page and the view (a field falling under 32 screen points, as a page does when zoomed out to fit, keeps its outline), snapshot tabs that reuse instead of multiplying, named-layer signature appearances redrawn for their real state (#79; display, the CLI's render, exports and printing, never saves; captions in the interface language, Chinese in English; the badge stays). Missing: timestamps (B-T), LTV (DSS, OCSP, CRL), FieldMDP, smart cards, PKCS #11 |
| M10 | OCR, create, export, print | 200–350 | 33% | 120–215 | Done: Windows Printer default sends native two-sided bindings only when automatic duplex support is confirmed, preserving manual driver defaults (#5130/#5772); physical output remains unqualified. DocumentDuplex-only bindings use the supported scope through craft-print 9fe50c94. create from blank/text/PNG/JPEG/TIFF (multi-page)/GIF/BMP; export PNG/JPEG/TIFF and text; Print (Acrobat's sizing, n-up, booklet, poster, comments & forms, preview, the CUPS and Windows spoolers, print-ready PDF).<br>Done since: embedded/72/custom DPI choices for image imports; export all images; OCR (searchable image for pages, ranges and multiple files); single-sided cut-and-stack imposition with cut marks, through Print and doc_print; architectural/ANSI/large ISO sheet presets, Match page at 100% and PDF-scale preview (#599 follow-up); Create PDF from multiple files (PDFs, images and text, edited as pages in the grid); OCR can also read pictures on pages that have text, without adding that text twice (#1028). Model staging fetches SHA-256-verified ONNX detection/recognition files after the upstream RTEN URL brownout; runtime discovery prefers complete ONNX pairs per directory, then complete legacy RTEN pairs, without mixing formats or changing directory precedence. Done since: Word invoice export uses borderless inferred tables, floating positioned text/artwork only where text overlaps an image or is rotated, and per-page sizes, covered by synthetic invoice automation regressions (#773); printing on the web through the browser's print dialog, sheets drawn by PdfCraft (gap 4). Lossless image export applies per-component Decode, including Indexed mapping (#1201 partial). Existing 16-bit precision and color-management limits remain. #3532 partial: vector printing retains existing appearances with absent Subtype through a print-owned Form copy; source artwork stays unchanged. #3120: Office export reads form XObjects 50 levels deep, as the renderer does, and up to 50 000 a page; pages past that are listed in `truncated_pages`. Word table cells use the grid columns they span, including cells after a merged cell. Done since: text in a table slot covered by another cell's span remains as paragraph text in Word, HTML, RTF and PowerPoint. Web OCR downloads its models on first use and reads in Web Workers with SIMD (`ocr.ocr-web`, shipped). Missing: OCR languages beyond Latin, web OCR near native speed, editable-text OCR output, full Office layout fidelity/import. Done since: dotted-leader contents entries stay in separate complete paragraphs (#1757) #2156 candidate: last selected print paper persists across sessions; other print preferences unchanged. Image export applies soft-mask Decode opacity intervals before resizing, including indirect entries and inverted ranges. Vector printing gives annotation appearances unoccupied XObject names so existing page artwork survives. Done since: Enhance scanned file cleans scanned pages in place (background removal, edge shadow removal, despeckle; the OCR text layer and comments stay; #4119 partial). |
| M10 | OCR, create, export, print | 200–350 | 33% | 120–215 | Done: Windows Printer default sends native two-sided bindings only when automatic duplex support is confirmed, preserving manual driver defaults (#5130/#5772); physical output remains unqualified. DocumentDuplex-only bindings use the supported scope through craft-print 9fe50c94. create from blank/text/PNG/JPEG/TIFF (multi-page)/GIF/BMP; export PNG/JPEG/TIFF and text; Print (Acrobat's sizing, n-up, booklet, poster, comments & forms, preview, the CUPS and Windows spoolers, print-ready PDF).<br>Done since: Word, Excel, PowerPoint, OpenDocument and RTF files open from File ▸ Open as new PDFs through an installed LibreOffice (`create.from-office` partial); embedded/72/custom DPI choices for image imports; export all images; OCR (searchable image for pages, ranges and multiple files); single-sided cut-and-stack imposition with cut marks, through Print and doc_print; architectural/ANSI/large ISO sheet presets, Match page at 100% and PDF-scale preview (#599 follow-up); Create PDF from multiple files (PDFs, images and text, edited as pages in the grid); OCR can also read pictures on pages that have text, without adding that text twice (#1028). Model staging fetches SHA-256-verified ONNX detection/recognition files after the upstream RTEN URL brownout; runtime discovery prefers complete ONNX pairs per directory, then complete legacy RTEN pairs, without mixing formats or changing directory precedence. Done since: Word invoice export uses borderless inferred tables, floating positioned text/artwork only where text overlaps an image or is rotated, and per-page sizes, covered by synthetic invoice automation regressions (#773); printing on the web through the browser's print dialog, sheets drawn by PdfCraft (gap 4). Lossless image export applies per-component Decode, including Indexed mapping (#1201 partial). Existing 16-bit precision and color-management limits remain. #3532 partial: vector printing retains existing appearances with absent Subtype through a print-owned Form copy; source artwork stays unchanged. #3120: Office export reads form XObjects 50 levels deep, as the renderer does, and up to 50 000 a page; pages past that are listed in `truncated_pages`. Word table cells use the grid columns they span, including cells after a merged cell. Done since: text in a table slot covered by another cell's span remains as paragraph text in Word, HTML, RTF and PowerPoint. Web OCR downloads its models on first use and reads in Web Workers with SIMD (`ocr.ocr-web`, shipped). Missing: OCR languages beyond Latin, web OCR near native speed, editable-text OCR output, full Office layout fidelity/import. Done since: dotted-leader contents entries stay in separate complete paragraphs (#1757) #2156 candidate: last selected print paper persists across sessions; other print preferences unchanged. Image export applies soft-mask Decode opacity intervals before resizing, including indirect entries and inverted ranges. Vector printing gives annotation appearances unoccupied XObject names so existing page artwork survives. Done since: Enhance scanned file cleans scanned pages in place (background removal, edge shadow removal, despeckle; the OCR text layer and comments stay; #4119 partial). |
| M11 | Optimize, preflight, PDF/A/X/UA, print production | 200–350 | 18% | 80–140 | Done: new `optimize` crate: Reduce File Size and the PDF Optimizer (images measured where drawn, bicubic downsampling, JPEG/ZIP recompression only when smaller, discard objects and user data, Flate clean-up, resource merging, object streams). Target-version compatibility is partial: newer content is not detected or converted. Content stream boundaries preserve comments, split operands and graphics state when image fragments merge. Image downsampling includes page-local `/UserUnit` scaling when measuring physical resolution. Done since: reject invalid image and soft-mask dimensions with checked sizing (#2178); black-and-white images stored losslessly as CCITT Group 4 when smaller, averaged down to 300 ppi above 450 (halftones aside), Group 3 stored again as Group 4, and black-and-white RGB and indexed images that use only palette entries 0 and 1 packed to one bit. Missing: fonts and transparency panels, space audit, preflight, PDF/A/X/UA |
| M8 | Security + redaction | 100–180 | 68% | 15–30 | Done: opening protected documents, permissions, Protect Using Password, Remove security; redaction (mark text/areas/pages, Search & Redact with patterns, apply removing glyphs, image pixels, vectors, XObject content, annotations and fields, verification, full rewrite on save); Remove hidden information and Sanitize.<br>Done since: redaction codes (U.S. FOIA and Privacy Act sets as overlay text, in Redaction properties and `redact_mark`). Redaction cleans indirect and form structure references, preserving surviving form tags. Redaction retires inherited resource dictionaries, so a parent `/Pages` can't keep a removed XObject reachable; unredacted pages are left untouched. Image redaction removes alternate images and unsupported masks only on affected images. Changed marked-content spans lose replacement descriptions; untouched spans keep theirs. Hidden-text cleanup retires unused named descriptions across inherited/shared page and form resources (64 focused tests and scoped Rust 1.96 Clippy/wasm checks pass; publication CI pending). The redaction audit counts a filled cover that hides half of any one word of a long line (#2662), applied or not. Phone-pattern redaction also matches labelled Italian geographic numbers with `+39` and a slash after the area code (#3205 follow-up); unlabelled international slash numbers remain unsupported. Missing: certificate security, custom redaction code sets and pattern locales, DCT re-encoding |
| M9 | Signatures (PAdES, validation) | 160–280 | 60% | 40–70 | Done: new `sign` crate (DER, X.509, CMS, PKCS #12 on RustCrypto; aws-lc-rs for RSA private keys); PAdES B-B signing (visible, invisible, existing fields, certification with DocMDP); validation with trust store and changes-after-signing classification; self-signed digital IDs; Signatures panel, message bar, sign dialogs; agent tools. Checked with pdfsig and OpenSSL.<br>Done since: macOS Keychain and Windows Current User Personal store signing (software-backed CNG keys), certificate viewer, signed documents protected from rewrites, page validation overlays separate from original signature artwork (baked Not Verified captions remain; #79 open), snapshot tabs that reuse instead of multiplying. Missing: timestamps (B-T), LTV (DSS, OCSP, CRL), FieldMDP, smart cards, PKCS #11 |
@@ -506,3 +522,5 @@ Estimates unchanged.
## Pages sidebar keyboard refresh (2026-10-30)
PR #2133 retains document-local Pages sidebar focus for Ctrl/Cmd+A and Escape after the UI module split. The selection does not navigate. The focused regression is in `crates/ui-egui/tests/it/view.rs`; current-head hosted validation is pending. Feature counts and estimates are unchanged.
- 2026-11-04: AcroForm review follow-up covers field-instance retirement across history, rejected-writeback realm invalidation, mixed widget icons, shared calculation budgets and multi-select/checkbox consistency, frozen-global events, exact image imports, comparison-half scripts, focus reveal and explicit busy/timeout feedback. Synthetic regressions and elapsed UI deadlines accompany the fixes.
+1 -1
View File
@@ -212,7 +212,7 @@ full parity in that area.
| G Protect, redact, sanitize | 41 | 23 | 2 | 58.5% | 55% | 15–30 | Passwords and permissions, Remove security, redaction that removes glyphs, pixels, vectors, annotations and tags, with verification; word lists, patterns, FOIA/Privacy Act codes; sanitize. Missing: certificate security, pattern locales, custom code sets, partial-word redaction, folder redaction, protected view |
| H Digital signatures | 45 | 23 | 10 | 62.2% | 45% | 40–70 | PAdES B-B signing and validation (PKCS #12, macOS Keychain, Windows store with PIN prompts), certification, encrypted documents, certificate usage checks; B-T, LTV, OCSP/CRL with caller-supplied evidence. Missing: PKCS #11 and smart cards outside Windows, timestamp-server preferences, online revocation fetching, FieldMDP and field locking, OS trust store, EU Trusted Lists, compare signed version |
| I Scan and OCR | 26 | 5 | 2 | 23.1% | 15% | 40–75 | Searchable image (ocrs, Latin script without accents) on pages, ranges and many files. Missing: every other language (#744 Dutch; accents, CJK, Cyrillic, Arabic), editable-text output, deskew, rotation, camera images beyond straightening (found or placed corners): orientation, clean-up, MRC compression, scanners. Background removal, edge shadow removal and despeckle work on scanned pages (Enhance scanned file) but not yet as options of Recognize text, nor on CMYK, indexed, JBIG2 or CCITT scans |
| J Create and export | 34 | 12 | 3 | 39.7% | 25% | 55–100 | Create from images, multipage TIFF, text, clipboard, blank, many files at once; export PNG/JPEG/TIFF, text, RTF; Word and HTML partial. Missing: Office/HTML/web/PostScript to PDF, Excel and PowerPoint export, Word layout fidelity (#773), SVG/XML/EPS/PS/JPEG 2000 export, accessible text |
| J Create and export | 34 | 12 | 3 | 39.7% | 25% | 55–100 | Create from images, multipage TIFF, text, clipboard, blank, many files at once; export PNG/JPEG/TIFF, text, RTF; Word and HTML partial. Office documents open through an installed LibreOffice (File ▸ Open only). Missing: HTML/web/PostScript to PDF, Excel and PowerPoint export, Word layout fidelity (#773), SVG/XML/EPS/PS/JPEG 2000 export, accessible text |
| K Optimize and standards | 58 | 15 | 11 | 35.3% | 20% | 65–115 | Reduce File Size and the Optimizer (images, CCITT and lossless JBIG2 monochrome compression, discards, clean-up, space audit); PDF/A-2b/3b verify and convert (partial). Missing: Preflight (profiles, fixups, reports), PDF/A-1/4 and a/u levels, PDF/X, PDF/UA, PDF/E, PDF/VT, transparency flattening, font unembedding, lossy/refinement JBIG2 |
| L Print | 39 | 15 | 1 | 39.7% | 35% | 30–55 | Acrobat's sizing, n-up, booklet, poster, comments & forms, preview, print-ready PDF, CUPS on macOS/Linux, the Windows spooler (#756) and the browser's print dialog on the web. Missing: print as image, PostScript, all print-production tools (output preview, separations, ink manager, printer marks, colour conversion) |
| M Accessibility | 61 | 38 | 1 | 63.1% | 30% | 40–70 | Accessibility Checker (all 32 rules, report, fixes), alternate text, title and language. Missing: autotag, Tags/Order/Content panels, Reading Order tool, artifact marking, table editor, Read Out Loud, full keyboard operation of the app, screen-reader audit |