Commit Graph
20 Commits
Author SHA1 Message Date
FredApps c0f5f92b57 Merge remote-tracking branch 'origin/main' into port-4751
# Conflicts:
#	crates/automation/src/dispatch.rs
#	crates/automation/src/tools.rs
#	crates/forms/src/lib.rs
#	crates/js/src/tests.rs
#	crates/ui-egui/src/workflows/js_ui.rs
#	parity/acrobat-features.toml
2026-11-11 13:11:32 +01:00
FredAppsandDWAA1660 059dbd6e00 feat(forms): open, Will Save and Will Print actions, widget pointer/focus scripts, setItems/setAction/toSource (port of #344) (#6969)
* feat(js, forms): field setItems, setAction and a bounded toSource (#269)

Ported from #344 by @DWAA1660 onto the per-document script session.

- `field.setItems(items)` replaces a combo or list box's items (text, or
  [text, export] pairs). The value stays when an item still has it, and
  otherwise becomes the first item's. Written as the field's /Opt (pairs
  only where the export value differs), /I dropped, field redrawn.
- `field.setAction(trigger, script)` installs a JavaScript action for one
  trigger (MouseUp/Down/Enter/Exit, OnFocus/OnBlur, Keystroke, Validate,
  Calculate, Format), replacing only that trigger's action.
- `Object.prototype.toSource()` writes plain data (strings, numbers,
  booleans, null, undefined, arrays, objects) that eval reads back. It
  refuses functions, cycles, more than 64 levels, 10,000 values or 1 MiB
  with an error the script can catch.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* feat(engine): run a form's open, Will Save and Will Print actions and its widgets' pointer and focus scripts (#269)

Ported from #344 by @DWAA1660 onto the per-document script session: each
runs in the document's retained realm, so state the document scripts and
earlier events left is kept.

- The catalog /OpenAction's JavaScript (and its /Next chain, bounded and
  cycle-safe; other action types skipped) runs once on opening, after the
  document scripts, for background opens and reloads too, and again on
  Revert in a fresh realm. Event Doc/Open.
- `Session::run_document_actions` runs /AA /WS (Will Save) or /AA /WP
  (Will Print). A failing action is reported and never stops the save or
  print. doc_save and doc_print run them first and report what they said
  (`will_save`, `will_print`).
- Scripts that run unasked (open, Will Save, Will Print, pointer over,
  focus) may only ask for highlight, zoom, focus or a page; a click's
  Mouse Down / Mouse Up keep their requests.
- `Session::field_trigger` runs a widget's Mouse Enter, Mouse Exit, Mouse
  Down, Mouse Up, Focus or Blur JavaScript (one undoable edit), reading only
  that widget's dictionary. `field_focus` now runs an AcroForm widget's On
  Focus / On Blur as well as XFA enter/exit.
- New tool `js_event`; form_focus's description says AcroForm focus
  scripts run too.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* feat(ui): Will Save before saving, Will Print before printing, and widget pointer scripts in the app (#269, #247)

Ported from #344 by @DWAA1660.

- Save (both paths) runs the document's Will Save actions first, and Print
  its Will Print actions; what they change is saved or printed, and what
  they say is shown.
- The pointer moving onto or off a form widget queues its Mouse Enter /
  Mouse Exit script, a press its Mouse Down. A click on a check box or radio
  button queues its Mouse Up after the toggle, so the script reads the value
  the click stored (#247). The queue runs after the frame's edit.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* docs(parity): document actions and widget triggers are partly supported (#269)

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* test: check toSource's text (scripts can't eval it) and the trigger event names (#269)

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* test: a check box's Mouse Up reads its new value; keep the fixtures on screen and the realm across an in-place save (#247, #269)

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* fix(engine, forms, js, ui): review fixes for document actions and field triggers

- widget trigger actions follow /Next, each action its own edit; errors don't stop the chain
- setAction installs on the field the object stands for when names repeat
- toSource refuses non-plain objects and a __proto__ key
- doc_print checks the destination before Will Print; js_event returns only its own output
- combo/list clicks run Mouse Up; a refused click's Mouse Up doesn't run
- label crops are re-read when Will Print changed the document

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* forms: run a pointer trigger on the field it hit and refresh the edited field's draft

A trigger now carries the field's object, so of fields sharing a name the one under the
pointer runs its own script (Session::field_trigger_target); js_event passes the field it
resolved. A trigger script changing the field being edited shows in its editor unless the
user already typed there, as focus scripts do.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* forms: carry the field's identity through focus scripts and js_event

Focus transitions now track the field object, so moving between fields sharing a name is a
move and the field that gained or lost focus runs its own script
(Session::field_focus_target). js_event takes an optional field_id from form_fields to pick
one of fields sharing a name.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

---------

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>
2026-11-11 13:09:37 +01:00
FredAppsandPeter Yatsyshin a0b4c88ba8 feat(forms): page_scripts reads a page's open/close actions and its annotations' page actions (#169)
Co-authored-by: Peter Yatsyshin <p.yatsyshin@googlemail.com>
2026-11-11 10:48:07 +01:00
Erkan DogruandFredApps f6c59c0b93 fix: integrate persistent AcroForm script workflows with native prompts and appearance writeback (#5699)
* AcroForm: integrate persistent timesheet script workflows

* AcroForm: address review caching, worker stacks and live metadata

* M6: address second review of AcroForm script workflows

* M6: handle top-level ASI boundaries and verify cyclic calculation results

* M6: bound script workers and isolate pending completions across edits

* M6: make form resets visible before subsequent script reads

* M6: include calculated dependents in reset comparison baselines

* M6: bound reset writeback work before native completion

* M6: charge expanded reset selections and field name bytes

* M6: bound string conversion bytes and charge reset widgets

* M6: preserve unread defaults and bound prompt text conversion

* M6: bound reset calls early and preserve calculated field identity

* AcroForm: address grouped calculations, field identity and widget ownership

* AcroForm: retire field instances and address native review edge cases

* AcroForm: route comparison Save As and Print to the source document

* M6: dispatch text widget mouse-up scripts

* M6: honor fit bounds for form button icons

* AcroForm: bound calculation work and preserve reset and save integrity

* Harden AcroForm script requests and worker recovery

* Keep form callbacks and focus consistent across edits

* AcroForm: preserve reset ordering and bound edit callbacks

* Fix reset appearance ordering and XFA focus after row removal

* Keep XFA focus indexes and stopped reset appearances consistent

* Document form script execution and reset helpers

* Keep script workers bounded and reject partial callback edits

* Keep form events available after a document script reaches VM limits

* Keep main's first-value Simple calculation and restore the calculateNow helper test

AFSimple_Calculate (JavaScript) and the native Simple calculation count each field's first
value again, as #5304 landed them; summing every selection of a multiple-selection list is
a separate behaviour change. Restores the document-helper calculateNow test and the
AFSimple_Calculate doc comment, and adds native and JavaScript multiple-selection cases.

* Patch the callback field snapshot in place instead of rebuilding it

Each field callback rebuilt all field states (3,129 on the benchmark form), and the
retained realm held the previous snapshot, so stamping instance tokens copied it again.
The runner now keeps its last snapshot and patches only the values and flags that
changed; ScriptSession::stamp_fields stamps it before dispatch, the worker copies only
when a token changes, and the realm drops the job's snapshot when the job ends (field
identity is checked against the cached keys instead).

* An oversized job script is that script's error, not a refused edit

Realm::run refused a script too long or too deep to run as an execution refusal, which
made the engine reject the whole edit (setting a 300 KiB calculation script failed). As on
main it is now a plain script error that keeps the value; the realm is not touched.

* Read a leading #! line as a comment in the chain scan; follow main's refusal and conditional limits in tests

The nesting guard from #5079 now checks brackets, prefixes and conditionals; the document
worker's preflight still bounds arrow-function and right-associative chains and skips a
leading #! line as the guard does. A refused script is an error, not a refused edit, so
the preflight test now checks the edit goes through with the error reported.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-11 08:42:04 +01:00
Harrison BoothandFredApps 832a51e6d7 Fix #5211: persist JavaScript field colours (#5499)
* Fix #5211: persist JavaScript field colours

* Read current document state in field scripts

* Redraw form fields after validation changes

* Format validation redraw regression test

* Keep field list alive in redraw test

* Refresh fields only after same-field scripts

* Format conditional field refresh

* Borrow accepted field value for storage

* Redraw calculated fields from current appearance

* Format JavaScript field state merge

* Restore JavaScript runner document state

* Group JavaScript runner form inputs

* Read current field display state in scripts

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 23:43:44 +01:00
Harrison BoothandFredApps d0b17ef95f Add JavaScript push-button caption methods (#5505)
* Add push-button caption script methods

* Apply scripted caption with other field changes

* Complete caption field property initializers

* Apply scripted captions to every button widget

* Format caption regression test

* Cover caption getter errors and redo state

* Assert button caption undo state

* Reject button caption writes during format events

* Test empty push-button captions

* Validate push-button caption updates

* Keep no-widget caption test data alive

* Check caption clipping marker

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 23:21:48 +01:00
Harrison BoothandFredApps c73a53705b Fix #5211: make calculateNow synchronous (#5304)
* Fix #5211: make calculateNow synchronous

* Bound and cache calculation execution

* fix(engine): invalidate field calculation cache

* perf(js): build calculation scripts lazily

* Refresh calculation cache after page insertion

* Load calculations for document script helpers

* Assert imported calculation result

* Initialize formatted field-change state

* Update formatted calculation test expectations

* Include the referenced field in calculation test

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 22:48:50 +01:00
mtlcl410andClaude Opus 5.5 2d8b7048a2 Merge upstream/main into fix/checkbox-kid-on-states
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:57:10 +08:00
Unhumanje ab7af72347 fix(forms): name tree nodes sharing one long /Kids array no longer exhaust memory (#5121) 2026-11-09 10:11:18 +08:00
mtlcl410 d65ae4268e M6: a calculate script that sets its own field through the field object keeps that value (form.custom-scripts) (#6616) 2026-11-09 08:59:19 +08:00
Harrison Booth 3b731f9458 Expose widget rectangles to field scripts (#6093) 2026-11-09 08:42:39 +08:00
mtlcl410 bf56dfe7af Merge remote-tracking branch 'upstream/main' into fix/checkbox-kid-on-states-l4
# Conflicts:
#	crates/xfdf/src/tests.rs
2026-11-06 01:04:02 +08:00
6427357fcb Forms: date fields name months in the date language, not always English (#5303) (#5464)
* Forms: date fields name months in the date language, not always English (#5303)

`AFDate_FormatEx("dd-mmm-yy")` wrote "18-Oct-26" in an Italian interface,
where a localized Acrobat writes "18-ott-26". Form date formats,
`util.printd` and `util.scand` now take their month and weekday names
from the date language (Preferences > Date format > Language, which
follows the interface language until one is picked), and still read
English names typed in. The names are process-wide, so their test runs
in its own binary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: dates read back in every date language (#5303)

Trying every date language showed what a field wrote not always reading
back: names holding digits ("10月"), spaces ("tháng 10"), dots ("ม.ค.")
or combining vowel signs (Thai, Hindi, Telugu) fell apart word by word,
and Italian, Spanish, Romanian and Albanian share some short weekday and
month names ("mar"). The language's names are now found whole in the
text first, the longest at each place, and read as their English names;
the format's order of `mmm` and `ddd` settles a shared name. Both
AFDate parsing and util.scand do this, on text up to 1 KB. A test reads
back every language, each name format, and hostile text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: date names no field font can draw are drawn in English (#5303)

With the names in the date language, a Russian dd-mmm-yy field drawn in
Helvetica showed "18-????.-26": no /DR font held "окт." and the WinAnsi
stand-in drew each letter as ?. The same held for Japanese, Chinese,
Greek, Arabic, Thai and others, and for Latin names outside WinAnsi
(Czech "říj"). Before, the names were English and always drew. An
AFDate/AFTime field now formats its value again with English names for
a widget whose fonts can't draw the localized text; a custom Format
script is run again with util.printd writing English names for such a
widget, and its result kept only when it draws whole. A field whose
font holds the names keeps them, and scripts otherwise still print the
date language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: an English weekday before the month keeps the month (#5303)

With Italian dates, "Tue 17 Mar 2026" in a "ddd d mmm yyyy" field read
as no date: the language's names were found in the text, but not
English ones, so "Tue" left the format's weekday unfilled and "Mar",
which Italian shares between Tuesday and March, was taken as the
weekday. Both AFDate parsing and util.scand lost the month, and
Spanish, Romanian and Albanian ("mar") did the same. English names are
now found too and fill their field of the format as localized ones do.
A test reads English and mixed weekdays before the month in those
languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: an English date run is drawn even when it prints the value (#5303)

A Helvetica field storing "18-Oct-26" whose Format script prints it
again as dd-mmm-yy still showed "18-???.-26" in Russian. The English
run printed "18-Oct-26", but it was dropped for equalling the stored
value, which was how a failed run was told apart: a failing script
handed back the value unchanged. The English run now says whether it
ran (`None` when the script failed or the runner can't write English
dates), and what it made is drawn whenever it draws whole. A test
reprints the stored value under Russian, and checks that a failing
script keeps its text and is reported undrawn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: a short weekday typed without its dot is the weekday (#5303)

With French dates, "mar 6 janv 2026" in a "ddd d mmm yyyy" field read
as no date through util.scand, and "mar 06/01/2026" in "ddd dd/mm/yyyy"
through AFDate too. French writes Tuesday as "mar.", which a name
search only found with its dot; since English names are found as well,
the English "Mar" (March) was the only name of that length, so the
weekday became a month. Romanian "mar" did the same. A short name
ending in a dot is now also found typed without it, as the same kind of
name, so the format's order of name fields decides between it and an
equal name of the other kind. Both copies of the name search change. A
test reads French and Romanian weekdays typed without their dot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: a shared name is the month when no weekday is typed (#5303)

With Italian dates, "17 mar 2026" in a "ddd d mmm yyyy" field read as
no date, nor did "mar 17, 2026" in "dddd, mmmm d, yyyy". AFDate leaves
weekdays out of what it reads (they are shown, not typed), but the name
search still expected the format's weekday first, so "mar", which
Italian shares between Tuesday and March, filled it and left no month.
Spanish and Albanian "mar" and Albanian "sht" (Saturday, September) did
the same, and the keystroke check refused those dates. Where such a
read finds no date, the names are now read again with no weekday
expected, so the shared name is the month; a typed weekday still comes
first ("mar 17 mar 2026"). util.scand reads its fields in order and
needs the weekday, so it is unchanged. A test types those dates with the
weekday left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: Turkish names typed in capitals are read (#5303)

With Turkish dates, "1 MAYIS 2026" and "1 EKİM 2026" in a d mmmm yyyy
field read as no date through AFDate and util.scand, and "SALI 6 OCAK
2026" through util.scand. Names were compared lower-cased, which turns
I into i, not the dotless ı of "Mayıs" and "Salı", and İ into i with a
combining dot above, which "Ekim" doesn't have. Names and typed text
are now folded alike, the capital I and the dotted and dotless i all as
i and the combining dot dropped, one piece for each char so positions
in the text stay aligned: in both copies of the name search, the AFDate
month reader and util.scand's name matcher. English reads as before. A
test types Turkish names in capitals and English ones as a check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Forms: Greek names typed in capitals are read again (#5303)

Folding names char by char turned a capital Σ into σ, so "ΙΑΝΟΥΆΡΙΟΣ"
no longer matched "Ιανουάριος", whose last letter is the final ς. The
fold now makes ς a σ on both sides, in the forms and JS parsers alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 16:12:04 +01:00
mtlcl410andClaude Opus 5.5 880ccd4c82 M6: a check box's kids with different on states tick one at a time (ISO 32000-2 §12.7.5.2.3, Table 230)
A check box field with kids whose on states are A and B could only ever
tick A. With A ticked, clicking B cleared the field, so B's tick flashed
and vanished, and with nothing ticked, clicking B ticked A. The click sent a
field-wide Check(!on), and write_value always turned on the first kid's on
state, even for a value naming B.

§12.7.5.2.3 makes the field's /V the on state of the kid that is on (/AS
deciding where they differ), and Table 230 lets kids be checked
independently.

- A click on a kid that is off sends its on-state name: it (and any kid
  sharing that state) turns on and the others off. A click on a kid that is
  on clears the field. "On" is the kid's /AS (Field::widget_on).
- write_value: a named state or /Opt export ticks the kids with that state.
  Check(true) keeps the kid that is on, else takes the first.
- Field::check_box_value maps a stored value to the state it names. Reset to
  /DV, script value changes and FDF/XFDF import use it.
- The display-only fallback ticks only the kids that are on.
- form_fields lists a check box's options and the checked one when its kids
  have more than one on state. form_fill takes the name.

Kids sharing an on state still tick together (EXAMPLE 2).

Tests: forms unit, ui-egui end-to-end clicks, automation form_fill and
xfdf import. Each fails with its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-31 13:59:31 +08:00
aiongg bb8ea65ffd fix(forms): script streams share one decode budget per reading
Each field action and document-script entry decoded its /JS stream
with a fresh 1 GiB allowance and kept its own copy, so many fields or
name-tree pairs naming one compressed stream retained gigabytes. A
reading of the fields, one field's actions, and the document scripts
each share a 64 MiB DecodeBudget for their script streams.
2026-10-22 16:57:55 +00:00
e451934f72 M6: calculation order: read, check, fix and set the order fields calculate in (#3708)
* M6: renaming a field renames it in the calculations that read it

A rename silently broke every formula that used the field: a sum dropped
it, notation read it as 0 and a script kept a stale value. set_props now
rewrites the name in each field's Calculate script, in the same edit:
AFSimple_Calculate field lists, simplified field notation (the BVCALC
comment and its translation) and getField calls naming it in one string
literal. The rest of each script is kept as written; names built at run
time, non-UTF-8 script streams and a name another field still has are
left alone, and a self-check keeps a script whose meaning would change.

Notation is read by one lexer shared by notation_js, eval_notation and
the rename. form_set_props returns formulas_updated; Field Properties
says how many calculations now use the new name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z

* M6: form_set_props leaves the renamed field out of formulas_updated by object

The renamed field was matched by the name given, but set_props stores it trimmed and under
its parent's prefix. When the same call changed the field's own formula, formulas_updated
listed the field as one the rename had rewritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z

* M6: calculation order: read, check, fix and set the order fields calculate in

A form's calculations run in its calculation order (/CO), which a field
joins when it gets a calculation, so a chain defined in the wrong order
shows stale values. calculation_order reads the order against the fields,
with what each formula reads (field lists, notation names, literal getField
names) and what is wrong: a field reading one that calculates later, a loop
(strongly connected components, found without recursion), a field /CO
leaves out, and /CO entries that calculate nothing. Fix order is a stable
topological sort that moves only what must move and leaves loops alone.
set_calculation_order writes /CO afresh; a name two fields share (a
malformed form) stands for the next of them in the current order.

Edit::SetCalculationOrder sets it in one undo step and recalculates, and
form_calculation_order reads, sets, moves one field or fixes the order
headlessly. An edited formula now keeps its field's place in /CO, a field
/CO lists twice calculates once, and recalculation runs each field by
object, so two fields sharing a name both calculate. The /CO code of the
Calculate tab, field scripts and recalculation goes through one place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-10-21 19:56:27 -04:00
Leo CamusandBrandon Thomas 15ca7e13ac fix(forms): read each JavaScript name tree node once (#2040)
document_scripts_named walked the catalog's /Names /JavaScript tree by
resolving and deep-cloning every kid onto its stack before looking at it.
The 10,000 cap counted nodes taken off the stack, not what went on it, so a
node whose /Kids lists itself k times pushed k full copies of itself (each
holding its k-entry /Kids array) per visit. A 6.5 KB file with k = 1000 made
`pdfcraft-cli run` (doc_open, js_document_scripts) exhaust an 8 GB address
space in 8 seconds and abort; the same walk runs when a field edit sets up
the script runner. A tree that lists a leaf twice returned its script twice.

Keep kids unresolved on the stack and skip references already read, as the
/Dests walker in organize does. The cap stays.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:40:06 -04:00
PiohrunandBrandon Thomas d576fe2b06 Forms: keep tiles and render unbaked field values during page re-render (#636)
* Forms: keep tiles and render unbaked field values during page re-render

In complex interactive forms, switching focus away from an edited field
caused its text to disappear for several seconds while background tiles
re-rendered.

- In DocView, retain existing tiles as stale rather than immediately
  purging them on page_changed so the canvas remains sharp without flashing.
- Prioritize visible tiles over backdrops in the render queue.
- Track unbaked form fields and paint an immediate egui widget preview
  for committed text, checkbox, and radio inputs until fresh raster
  tiles arrive.
- Scope page invalidation on form edits to pages containing the edited
  or calculated fields.
- Hoist fields lookup in recalculate_with and apply_changes to eliminate
  repeated parsing.
- Run JavaScript scripts via catch_unwind on the current thread instead of
  spawning a new thread per script invocation.
- Add contributor entry for Piohrun in contributors/people.toml.
- Add test switching_fields_keeps_committed_text_visible_immediately.

* Forms: address review feedback on tile stale tracking, hoist, and diff invalidation

- Revert crates/js to preserve dedicated 1 GiB stack thread for boa scripts.
- In DocView::page_changed, purge non-current-tag tiles and mark only current-tag tiles stale.
- Restore demand queue order in prepare_render_queue.
- Add unbaked overlay entries only on successful edit apply and only for fields whose values changed; fix font zoom calculation on 90°/270° pages.
- Re-read fields(doc) in recalculate_with only when script returns changes.
- Diff form before and after edit in apply_edit, invalidating pages with changed fields, falling back to document_changed for XFA or page-count changes.
- Add tests covering script recalculation side effects across pages, unbaked overlay cleanup on tile delivery/zoom/rejection, and checkbox recalc chaining.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 15:15:48 -04:00
Brandon ThomasandClaude Opus 5.5 d3db99818f Rename PrintCraft to PdfCraft (#174)
The app, crates, binaries, bundle id, packaging, icons, screenshots and docs are now
PdfCraft / pdfcraft (repo storytold/pdfcraft). On first launch the desktop app moves the
PrintCraft settings and crash-recovery folders to the new name, and both apps read settings
saved under the old storage key, so upgrades keep recent files, preferences and unsaved work.
The signing test fixtures keep their PrintCraft subject names (they are binary).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:03:57 -04:00
Brandon ThomasandClaude Opus 5.5 06f8d73ec7 M6: Acrobat JavaScript: sandboxed boa engine with the forms object model (printcraft-js); custom keystroke/validate/calculate/format scripts, button scripts, document JavaScripts, JavaScript console (⌘J), Preferences ▸ Enable JavaScript; js_run, js_document_scripts, js_set_document_script, form_set_script, js_enabled tools
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:41:47 -04:00