Commit Graph
90 Commits
Author SHA1 Message Date
bflatastic 720a841981 Merge #5393: M12: a picture an XFA form links is drawn from the PDF's XFAImages name tree (#5392)
# Conflicts:
#	crates/xfa/src/fixtures.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:40:50 +07:00
bflatastic 9e55f61de0 Merge #5369: M12: an XFA subformSet is transparent to layout, so its members sit where their container puts them (#5366)
# Conflicts:
#	crates/xfa/src/layout/measure.rs
#	crates/xfa/src/parse.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:39:26 +07:00
bflatastic ab2d4382ff Merge #5368: M12: XFA master-page fields keep their own value on each page (#5365)
# Conflicts:
#	crates/engine/src/xfa.rs
#	crates/xfa/src/layout/pages.rs
#	crates/xfa/src/lib.rs
#	crates/xfa/src/script.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:38:21 +07:00
bflatastic 50f174afb7 Merge #5010: M12: XFA scripting object model (1/3): scripts read template properties as XFA 3.3 defines them
# Conflicts:
#	crates/engine/src/xfa.rs
#	crates/js/src/xfa/host.rs
#	crates/xfa/src/packets.rs
#	crates/xfa/src/parse.rs
#	crates/xfa/src/tests.rs
#	parity/acrobat-features.toml
2026-11-09 18:30:30 +07:00
bflatastic c17990f0ea Merge #4662: M12: an XFA template's use/usehref prototypes are resolved before it is parsed (form.xfa-dynamic)
# Conflicts:
#	parity/acrobat-features.toml
2026-11-09 18:23:20 +07:00
bflatastic 3394f3077b Merge #4537: XFA: fields and exclusion groups that must be filled in are Required
# Conflicts:
#	crates/xfa/src/layout/draw.rs
#	crates/xfa/src/layout/field.rs
#	crates/xfa/src/layout/items.rs
#	parity/acrobat-features.toml
2026-11-09 18:22:54 +07:00
bflatastic eb33f633e3 Merge #4336: M12: screen-only and print-only XFA objects are shown and printed as the form says
# Conflicts:
#	crates/xfa/src/layout.rs
#	crates/xfa/src/pdf/strip.rs
#	parity/acrobat-features.toml
2026-11-09 18:18:07 +07:00
bflatastic 7976fff3c7 Merge #3589: Forms: app.runtimeHighlight, and XFA layout:ready scripts run on opening
# Conflicts:
#	crates/ui-egui/src/app.rs
#	crates/xfa/src/lib.rs
2026-11-09 18:16:03 +07:00
mtlcl410 b4e295afcb Merge upstream/main into fix/xfa-images-name-tree 2026-11-09 18:54:13 +08:00
mtlcl410 02f6138e6c Merge upstream/main into fix/xfa-subformset-transparent 2026-11-09 18:51:00 +08:00
mtlcl410 61062358e6 Merge remote-tracking branch 'upstream/main' into float-5368b
# Conflicts:
#	crates/xfa/src/lib.rs
#	crates/xfa/src/model.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:50:26 +08:00
mtlcl410 423cc239a1 M12: an XFA breakBefore or breakAfter without a targetType starts no new page (form.xfa-dynamic) (#5145) 2026-11-09 18:28:53 +08:00
mtlcl410 5276757b57 M12: a page break splits an empty XFA text field that grows with its text (form.xfa-dynamic) (#5525) 2026-11-09 18:14:39 +08:00
Eric Kaden 5d3d73461d Read a FormCalc script's layout methods without regard to case 2026-11-08 23:27:04 -05:00
mtlcl410andClaude Opus 5.5 aedb920bac Merge upstream/main into feat/xfa-usehref-prototypes
parity: keep main's form.xfa-layout notes and evidence, with the
prototype sentence and tests added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 11:35:53 +08:00
mtlcl410andClaude Opus 5.5 95be781b5c Merge upstream/main into feat/xfa-property-objects
Conflicts: xfa Template keeps main's embedded/embedded_paths and this
PR's cache (parse() sets all three); engine xfa.rs tests keep both
sides' tests; xfa README combines main's sheet numbers with this PR's
property objects; parity form.xfa-dynamic keeps this PR's notes plus
main's floating-field sentences and both sides' evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:58:05 +08:00
mtlcl410andClaude Opus 5.5 4810acb5af Merge upstream/main into fix/xfa-page-master-som-index
Union main's embedded-field exports and data_path import with the page-area ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:17:01 +08:00
mtlcl410andClaude Opus 5.5 cc89da6c76 Merge upstream/main into fix/xfa-images-name-tree
Pass main's SOM scope to draw_value_text and keep the image href pattern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 09:57:53 +08:00
Eric Kaden 643cbaa7f4 Merge remote-tracking branch 'upstream/main' into fix/xfa-required-fields
# Conflicts:
#	parity/acrobat-features.toml
2026-11-08 20:07:08 -05:00
Eric Kaden 432d614ae7 Merge remote-tracking branch 'upstream/main' into fix/xfa-relevant-print
# Conflicts:
#	crates/xfa/src/layout/pages.rs
#	parity/acrobat-features.toml
2026-11-08 19:52:02 -05:00
Eric Kaden ce9f73d6f3 Merge remote-tracking branch 'upstream/main' into fix/xfa-runtime-highlight
# Conflicts:
#	crates/engine/src/xfa.rs
#	crates/xfa/src/parse.rs
2026-11-08 19:51:16 -05:00
mtlcl410 0a029f0aad M12: floating fields show the values scripts give them, and the sheet number and count (#6160) (#6166) 2026-11-09 08:45:03 +08:00
mtlcl410 359cef8b0d M12: an XFA page area keeps at most 128 content areas, so a subform split across them lays out within its budget (#5843) (#5844) 2026-11-09 08:39:31 +08:00
Eric Kaden 0c37ffdc70 Merge remote-tracking branch 'upstream/main' into fix/xfa-runtime-highlight
# Conflicts:
#	crates/engine/src/js.rs
2026-11-05 10:30:38 -05:00
Dylan M. Taylor 76260bb6a5 fix(xfa): scripts come only from the XDP's own packets and quote the date pattern (#5987)
The template (and the saved form state) was found anywhere under the XDP root, so a
template-shaped element inside imported datasets could supply the form and its event
scripts. A date field's pattern went into AFDate_FormatEx/KeystrokeEx string literals
unescaped.
2026-11-03 14:33:41 -05:00
mtlcl410andClaude Opus 5.5 fcd72a3d6a Merge storytold/pdfcraft main into feat/xfa-property-objects
Conflicts:
- crates/xfa/src/parse.rs: main added quarter_turns before parse_common
  (and rotate in its body); this branch gave parse_common a warnings
  parameter for the property trees. Kept quarter_turns and the branch's
  signature; the body holds both props and rotate.
- crates/xfa/src/tests.rs: both sides added tests at the end of the file
  (this branch's packet-size test; main's rotated-draw tests). Kept both.
- parity/acrobat-features.toml (form.xfa-dynamic): main put the
  rotated-draw test at the front of the evidence and a sentence at the
  front of the notes; kept this branch's lines with main's additions
  prepended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyG234xeYkJfr41E6iP
2026-11-02 22:32:31 +08:00
mtlcl410andClaude Opus 5.5 9d3226ea49 Merge storytold/pdfcraft main into feat/xfa-usehref-prototypes
Conflicts:
- parity/acrobat-features.toml (form.xfa-dynamic): main added the
  rotated-draw test to the front of the evidence and a sentence to the
  front of the notes; this branch appended the prototype tests to the
  evidence and put "prototypes first: ..." into the layout sentence of
  the notes. Kept both on main's lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyG234xeYkJfr41E6iP
2026-11-02 22:31:28 +08:00
mtlcl410andClaude Opus 5.5 dbe2580f0e Merge storytold/pdfcraft main into fix/xfa-subformset-transparent
Conflicts:
- crates/xfa/src/tests.rs: both sides appended tests at the end of the
  file; kept this branch's subformSet tests and main's draw/anchor
  tests, closing the branch's last test before main's block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyG234xeYkJfr41E6iP
2026-11-02 22:22:55 +08:00
mtlcl410andClaude Opus 5.5 fb12804fe0 Merge storytold/pdfcraft main into fix/xfa-page-master-som-index
Conflicts:
- crates/engine/src/tests.rs: both sides appended tests at the end of
  the file; kept this branch's page-master tests and main's page and
  image edit tests, closing the branch's last test before main's block.
- crates/xfa/src/tests.rs: the same, with main's draw/anchor tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyG234xeYkJfr41E6iP
2026-11-02 22:22:35 +08:00
mtlcl410andClaude Opus 5.5 fb168ada0a Merge storytold/pdfcraft main into fix/xfa-images-name-tree
Conflicts:
- crates/xfa/src/tests.rs: both sides added tests at the end of the file
  (this branch's XFAImages name-tree tests; main's rotated-draw tests).
  Kept both, closing the branch's last test before main's helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyG234xeYkJfr41E6iP
2026-11-02 22:22:28 +08:00
Eric Kaden 6e2675276e Merge remote-tracking branch 'upstream/main' into fix/xfa-required-fields
# Conflicts:
#	parity/acrobat-features.toml
2026-11-01 15:14:25 -05:00
Eric Kaden c01353479c Merge remote-tracking branch 'upstream/main' into fix/xfa-relevant-print
# Conflicts:
#	crates/xfa/src/layout.rs
#	crates/xfa/src/layout/common.rs
#	crates/xfa/src/layout/items.rs
#	crates/xfa/src/parse.rs
#	crates/xfa/src/pdf.rs
#	parity/acrobat-features.toml
2026-11-01 15:12:47 -05:00
mtlcl410andClaude Opus 5.5 607a5c26cd M12: XFA template parsing options built for each parse
roxmltree's ParsingOptions on main is no longer Copy, so the options used for the
template and again for the expanded template are built for each parse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-01 23:01:04 +08:00
mtlcl410 111c8f64e0 Merge branch 'main' of https://github.com/storytold/pdfcraft into feat/xfa-usehref-prototypes 2026-11-01 22:59:32 +08:00
Grzegorz MartensandClaude Opus 5.5 2afbf55cd2 M12: an XFA draw placed by position turns about its anchor
Nothing read rotate, so a turned line, picture or label was drawn as if it
stood upright: part of #5561. XFA gives the angle in degrees counter-clockwise
about the anchor, and Acrobat's flattened page for that issue's form puts the
three turned lines exactly there, which the test holds it to.

A turned draw is one item: a page break moves it whole, and what it holds is
written unturned under one matrix, so text, pictures and borders need nothing
of their own. The page count and link annotations look inside it, and its
container grows to the box it stands in. Fields, subforms and draws in a flow
don't turn yet.

That issue's own form is static, so it already shows Acrobat's page: this is
for dynamic forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-01 02:59:32 +01:00
mtlcl410andClaude Opus 5.5 63a77901e6 M12: an XFA page master's content is its own on every page, in the layout and in scripts
Master-page content repeated on each page shared one SOM path (`master[0]`), so its fields
shared one data node and one script node. Each page's copy is now `master[n]`:
- the layout names it so;
- data binds it per page, except a field bound globally or by explicit ref, which stays shared
  (XFA 3.3, "Page Background");
- the live form has one page-area node per page laid out from it, counted from the generated
  fields, so a button on page 2 runs its click script as itself.
- an event listed before a script laid the form out again can name an object that is gone (a
  page the form no longer fills): it is not run, rather than run with `this` at the form's root,
  where `this.presence = "hidden"` hid the whole form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBVyQEwtmEw3mS1MoAiwrK
2026-10-31 07:20:36 +08:00
Eric Kaden 937618f4b7 Merge remote-tracking branch 'upstream/main' into fix/xfa-runtime-highlight
# Conflicts:
#	crates/engine/src/tests.rs
#	crates/ui-egui/src/app.rs
2026-10-30 17:55:37 -04:00
mtlcl410andClaude Opus 5.5 d3fdc2f41c M12: an XFA template parses up to what a packet the reader accepts can come to as text
A UTF-16 packet within the reader's 64 MiB grows by up to half when it
becomes UTF-8, so parse() now takes up to 96 MiB rather than 64: a packet
read_packets accepts always parses. The refusal gives the size in bytes
(review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-10-30 21:39:34 +08:00
mtlcl410andClaude Opus 5.5 0481b98cda M12: an XFA template parsed directly is held to the packet size limit
parse() refuses a template larger than the 64 MiB a document's packets may
hold (read_packets' limit), before any of it is parsed, so what parsing
keeps of it, item lists held by the field and its properties among them,
stays within a few times that whoever calls it (review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-10-30 20:51:57 +08:00
mtlcl410andClaude Opus 5.5 a3382ca1f9 M12: an XFA subformSet's members flow among their lr-tb parent's other children
In a flowing (lr-tb/rl-tb) parent the members of a subformSet now go on
along the parent's line with its other children, rather than the set being
packed as one block that moves to the next line whole (XFA 3.3 p. 357: the
set is transparent to layout). The members keep the set in their SOM paths.
Adds a test that, in a table and in one of its rows, a set's members still
go one under another (review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-10-30 19:34:56 +08:00
Dylan M. Taylor f76dbb9e8c deps: bump tar, zip, roxmltree, rustls-webpki and symphonia to their latest versions (#5381)
- `zip`: 9.1.0 → 9.1.3

Requirement `roxmltree` 0.20 → 0.21: `ParsingOptions` gained `entity_resolver` (initializers take `..Default::default()`, i.e. none, as before) and is no longer `Copy` (two functions build it with a closure, the pattern merge.rs already used).
2026-10-29 22:21:27 -04:00
mtlcl410andClaude Opus 5.5 a50e1491a4 M12: a picture an XFA form links is drawn from the PDF's XFAImages name tree
A template's <image href="…"> without inline data is now found in the
catalog's /Names /XFAImages tree, keyed by the URI the template writes
(XFA 3.3 pp. 257-258, "Storing Images Inside PDF"). Only that tree is
looked in; a link is never followed outside the file. The tree walk is
bounded (depth, nodes, names, a seen-set), and so are the bytes read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-10-30 08:59:03 +08:00
8e85e0b7ab M12: resolve XFA use/usehref prototypes before parsing the template
Designer style sheets give fields and draws their size, anchor, borders,
fills, fonts and caption placement through usehref references to elements
in a <proto> subform (XFA 3.3, chapter 6, "Prototypes"). The parser did
not follow them, so such forms lost their section bars, input boxes and
check-box placement.

The new crates/xfa/src/proto.rs writes the template again with every
reference into the same template merged in, and parse() reads that:
- references: usehref "#id", "#som(expr)" (with or without the legacy
  "."), and use "#id" or a SOM expression; usehref wins over use;
- rules from "Overriding Prototype Properties": own attributes, data
  content and children win; id and name are not inherited; children
  match by class and position, and a matched prototype child is the
  prototype of the element's own; a ui widget or value kind and rich
  text come from the nearest layer that has one;
- prototypes in other documents are not followed (no file or network
  access); a missing target, a loop (Example 6.8) or a long chain is
  left out with a warning;
- caps: 32 merged layers, 256 levels of nesting, 2 million elements of
  SOM search, and growth to 4x the template (1 MB floor, 64 MB ceiling);
  past a cap the template is read unresolved, with a warning.

The resolver is ported from the closed #1566 by EULArule, who invited
reuse, and changed: the name attribute is no longer inherited, use takes
SOM expressions, ids and SOM look-ups are indexed and cached under one
search budget, loops through ancestors are cut, the output cap is
relative, every fallback is a warning, and the saved form state is
still read from the original XDP.

Part of #3432.

Co-authored-by: EULA <90616501+EULArule@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-30 07:25:51 +08:00
mtlcl410andClaude Opus 5.5 8d40450d22 M12: XFA border corners not listed are drawn the way edges are
XFA 3.3 p. 38 gives a border's corners in the order top-left, top-right,
bottom-right, bottom-left, as its edges go top, right, bottom, left, and
says that when fewer than four are listed the last one listed is reused
for the rest. Readers don't do that: every reader tested draws a side not
listed from the edge or corner on the same side, one listed serving all
four sides, two alternating (0, 1, 0, 1), three reusing the second for
the fourth (0, 1, 2, 1). The layout already drew edges the readers' way,
but drew corners as p. 38's text says, so with corners [round, square]
only the top-left was round where readers round the top-left and the
bottom-right.

Corners now follow the readers too, through one helper, drawn_side, that
edges and corners share: with [round, square] the top-left and
bottom-right are round and the top-right and bottom-left square. Edges
are drawn as before. The scripting host's reads of a corner past those
listed (from the previous commit) mirror the same pattern, so a script
reads the corner the page draws.

Real forms (358 public forms with XFA, templates read through the
engine): 337 borders in 51 forms list two or three corners that differ,
all only in a square corner's presence, which the layout doesn't draw
differently; none has a rounded or inverted corner that would move, so
no rendered page changes.

Tests: one, two and three corners, two in both orders, and the
rounded-corners test's two-corner box (now [r1, r2, r1, r2]) fail with
the change reverted; a script test reads corners past those listed by
the same pattern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBVyQEwtmEw3mS1MoAiwrK
2026-10-29 17:56:53 +08:00
mtlcl410andClaude Opus 5.5 34f3716f5e M12: XFA scripts read a field's border, ui, validate and other properties (XFA 3.3 ch. 3, ch. 17)
Scripts got an object that takes anything for `this.border`, `this.ui`,
`this.validate` and the like (XFA 3.3 ch. 3, "Properties vs. Children",
p. 78), so `ui.oneOfChild.className == "choiceList"` was never true,
`this.validate.nullTest == "error"` never matched, and
`border.getElement("edge", 0).color.value` threw a TypeError.

The parser now keeps each object's own attributes and its property
elements (border, font, para, margin, ui, validate, occur, break,
breakBefore, breakAfter, assist, format, value, bind, desc, extras, keep,
caption, items, calculate) as a generic tree, bounded per object: 256
elements, 16 KiB of text, 8 levels, 64 attributes an element; past that
the rest is left out with one warning. An element keeps only its own
text, not that of children left out (an extras, a script); a validation
or a calculation keeps its first script with its text outside the
budget, as forms read it, within the script engine's 256 KiB limit on a
script. A field's first two `<items>` (a field has two at most, p. 726)
are kept as lists: each counts against the budget as one element, and
its entries, outside it, are bounded by the list's own limits (1,000
entries), as the field's lists are; a further `<items>` (one public form
has a third, empty one) is an ordinary property, every entry charged.
The tree is shared through an Arc by the template, every instance of the
object and every script's snapshot, and the engine keeps one converted
copy of each tree in the form's template, so it is made once per opened
form rather than per event or per script, behind the form's own lock,
and goes when the form is closed. On a 10,000-field form with 17
property elements each, a script's snapshot takes 3.24 ms against
1.46 ms on main (median of 15 interleaved runs); the property trees are
converted once per form, and each later snapshot adds one walk over the
form and a lookup per tree.

In JavaScript a property object is a Proxy naming its node and path. It
reads what the template writes, or the default XFA 3.3 gives for the
properties the 367 inventoried public forms read: presence of border,
edge, corner, fill (p. 629, 687, 663, 734); colours (a fill white,
p. 734; a font fill, edges and corners black, p. 734, 687, 663); font
typeface/size/weight (pp. 741-744); para hAlign/vAlign (p. 800); margin
insets (p. 776); validate nullTest/scriptTest/formatTest (pp. 880-881);
occur min/max/initial (p. 786); break before/after (p. 632); bind ref
(p. 623); keep next/previous none and intact by container (pp. 762-763);
a comb's numberOfCells (p. 657); a gradient's type (p. 770, 806, 820); a
fill's type solid (p. 734) and a field's widget textEdit (p. 877); a
calculation's override, "disabled" where the template has no calculate
element and "error" where it has one that doesn't say (pp. 641-642).

Where XFA 3.3 leaves the outcome open, or where every reader tested
contradicts its text, reads follow the readers. A border's presence
reads "hidden" when the object has no border element ("visible" with
one that doesn't say, p. 629). For the edges and corners a border
doesn't list, p. 38 says the last one listed is reused; readers draw
such a side from the edge or corner on the same side instead (two
listed alternate, three reuse the second for the fourth, one serves
every side), and reads follow the readers: a side past those listed
reads as the one drawn there (with none listed, the defaults: black,
0.5pt, solid, a square cap, a corner's radius 0in, square join, not
inverted, p. 687, p. 663), and is made as a copy of it (with what a
script already set on it) when a script reaches it, so a write then
changes only that side. A border has four sides: a fifth or later edge
or corner, even one the template lists, is out of reach, as the layout
draws four. The copies have their own budget (8 MiB a run), apart from
the run's text budget, so reaching sides never crowds out a script's
writes; past it a side still reads as the one drawn there. A fill type
the fill doesn't have (its pattern when it is solid, p. 734) can't be
read: the read raises an error the script can catch (GeneralError,
"operation failed"), the fill keeps its type, and `"pattern" in fill`
is false. Anything else unspecified reads as an empty value that
compares as "" and never throws; further defaults are added as forms
need them.

Property objects have className, oneOfChild, children by name
(desc.vMandatory, message.scriptTest) and by class (ui["#textEdit"]),
getElement(name, index), isPropertySpecified, getAttribute,
setAttribute, rawValue (a desc's first text), a validate's or
calculate's script text and message (p. 827), resolveNode(s),
somExpression and nodes (length/item/namedItem); nodes get getAttribute,
getElement, isPropertySpecified, and mandatory, validationMessage,
formatMessage and mandatoryMessage read from their validate's nullTest
and message texts (the text of that name, as the template or a script
named it, else a single unnamed one, which serves every test: p. 782,
p. 880). Setting a test's message sets its named text, else overwrites a
single unnamed one (so every test then reads it), else adds a text of
that name, as readers do; reading validate.message.<test> where no such
text exists raises a script error, as in readers. fillColor, fontColor
and borderColor read from the border fill, font fill and first edge
colours; borderColor set colours all four edges. A child of the same
name comes before a property, an attribute or a member, on nodes and on
property objects alike, for reads and writes (a write to a member a
child shadows leaves the property alone), as XFA SOM resolves name
clashes in favour of the explicitly named object, and `#name` reaches
the property (p. 99). A SOM expression from a node, or from
xfa.resolveNode, that runs into objects' properties
(resolveNode("ui.#textEdit"), "border.edge[1]",
"row[*].border.fill.color", "extras.arrayGroup[*].groupName", "ui.*")
gives those property objects, each instance's, 4,096 at most (about 30
of the forms do this); `value.#text` on a draw stays null, as before,
until a node's value is its value element.

Writes (border.fill.color.value = ..., validate.nullTest = ...,
rawValue, setAttribute, mandatory, validationMessage = ...) are kept for
the rest of the script run and read back through the same properties,
bounded by the run's text budget and 64 attributes a property, but have
no effect on the form yet.

The caption (XFA 3.3 p. 643) and a field's items (p. 758) are property
objects too. A caption's value holds its label as text or rich text,
read by name, by class (`['#text']`), through resolveNode and
oneOfChild; rich text reads as its words; the content a value doesn't
hold reads as null (one of them, p. 598). Items list their entries
through `nodes`, each with the text the choice list or check button
shows. A field's `items` is its first `<items>` (the shown list), a
second is reached by index (`items[1]`), and a field with none reads an
items object with no entries. Items reads follow the template:
`HNode::items` is the hook PdfCraft's choice-list methods (addItem,
clearItems; not on main yet) will set, so that reads agree with them
once they land. exData.loadXML and saveXML do nothing yet. Over the 163
dynamic forms (118,208 objects) no object reaches the budget: elements
p99 30, max 41 (limit 256); bytes p99 773, max 8,500 (limit
16 KiB); item entries per list p99 51, max 342. The choice-list
methods (selectedIndex reads still give an object that takes anything),
execEvent and script objects are handled separately. The one known gap
is value.text and value.exData (about 15 of the 367 forms): a node's
value stays its raw value, and changing what value returns is left to a
follow-up with a full-corpus regression check. FormCalc does not read
these properties yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBVyQEwtmEw3mS1MoAiwrK
2026-10-29 17:56:53 +08:00
mtlcl410andClaude Opus 5.5 ab2db45dfc M12: an XFA subformSet is transparent to layout, so its members sit where their container puts them
A subformSet was always laid out top to bottom. In a positioned subform its members, which have
no x or y of their own, were stacked instead of overlapping at their positions, so a two-column
section pushed its second column past the page (XFA 3.3 p. 357: "The layout process treats
subformSet objects as transparent"). A set now takes its container's layout; in a table or a row
its members still go one under another.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBVyQEwtmEw3mS1MoAiwrK
2026-10-29 13:34:40 +08:00
Eric Kaden 4b66608303 M6: doc comments on the XFA functions the required flag touches
draw_draw and parse_subform, which this branch changes, had none (CodeRabbit's docstring coverage).
2026-10-25 10:23:07 -04:00
Eric Kaden 0b7839d1e3 M12: the page-number assignment scan reads a bounded number of bounded expressions 2026-10-24 16:17:13 -04:00
Eric Kaden 9b5598b071 M12: a layout:ready assignment written over several lines is read whole 2026-10-24 16:10:18 -04:00
Eric Kaden 2cdc785c60 M12: only a field its layout:ready script gives a page call's result is numbered by the layout 2026-10-24 15:50:52 -04:00