Commit Graph
6087 Commits
Author SHA1 Message Date
Grzegorz MartensandClaude Opus 5.5 4fca5783a7 M7: a Thai edit refuses only a read past its allowance, and reads its subsets' metrics within it
CodeRabbit's review of 674a20899:

- The Thai edit refused when nothing was left of its allowance, so an
  edit whose reads spent it to the byte was refused. It now refuses when
  a read was refused for want of it (`DecodeBudget::refused`).
- `LegacyThaiFont::from_pdf` read its metrics with `from_dict`, outside
  the edit's allowance, and so did the edit for each subset it measures.
  Both now read them within it (`from_dict_within`), and metrics read
  past it adapt nothing: `from_pdf` refuses the font before reading its
  program, and the edit leaves the subset unread and is refused. The
  edit's scan of every font on the page for its face name stays outside
  the allowance, as on `main`: charged, each alias of a font would pay
  again, and other faces would spend what the face's subsets need.
  `MAX_THAI_PROGRAMS` says what it pays for.
- When the `/CIDToGIDMap` a THSarabunPSK subset's Thai mappings are
  rebuilt from was left unread for want of the editor's allowance,
  `composite_glyph_maps` gave nothing, and a subset with a Latin-only
  cmap of its own was listed as it was, without its Thai mappings. The
  editor now lists no copy of it, as for metrics read past the
  allowance.

Tests: `a_thai_edit_that_spends_its_allowance_to_the_byte_goes_through`
(`pdfcraft-edit`: a subset costing exactly the 64 MiB is edited, and
one three bytes past it is refused),
`a_legacy_fonts_metrics_are_paid_for_from_the_edits_allowance` and
`a_map_left_unread_for_the_allowance_lists_no_copy` (`pdfcraft-fonts`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 21:45:29 +01:00
Grzegorz MartensandClaude Opus 5.5 8c5333bfe7 M7: font reads decode through a view of the stream, and refuse before resolving its parameters
CodeRabbit's review of 674a20899:

- Each budgeted read of a font's stream went through
  `Document::with_direct_parms`, which copies the whole stream when its
  /DecodeParms is given by reference: an unrelated value in the
  dictionary, however large, was copied again for every read.
  `DecodeBudget::decode_stages_within` now takes the document and reads
  through `Document::decoding_view`, as `decode_strict` and
  `decode_with_damage` do: the data is shared, and only /Filter and the
  parameters made direct are copied. `Reads::decode` and the editor's
  program read use it, a Type 3 font's glyph descriptions read through
  `decode_with_damage`, and so does Repair Thai maps' program read.
- A Type 3 font's glyph descriptions resolved one description's
  parameters a font before the spent allowance refused it. They now ask
  `DecodeBudget::refuses` first, as `decode_stages_within` does for the
  others: once the allowance is spent a stream with data is refused, and
  noted, before its parameters are resolved.
- `DecodeBudget::refused` counts the decodes refused for want of the
  allowance, where `remaining() == 0` can't tell a refusal from an
  allowance spent to the byte.

Test: `reading_a_fonts_streams_copies_no_dictionary` (`pdfcraft-fonts`,
a test binary of its own): a font whose program and ToUnicode hold an
unrelated array of a million numbers and /DecodeParms by reference is
read eight times by the editor's program read and the metrics read,
and the process's peak grows by less than half of what one array
holds. That a Type 3 font refuses before resolving parameters changes
no result, only the time it takes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 21:45:10 +01:00
Grzegorz MartensandClaude Opus 5.5 674a208990 M7: a test pins that Repair Thai maps refuses a map that isn't Identity
The Legal forms review of b7224cbc9..4d6ff291d:

- Nothing pinned that `recovered_thai_map`'s rewritten guard still
  refuses a /CIDToGIDMap that isn't Identity: deleting it left every
  Thai test passing. `recovered_thai_map_checks_every_kept_glyph_against_
  the_font_program` now asserts no map for a map stream that moves glyphs
  and for another name.
- The guard's comment and the test's cited Identity as the default of
  ISO 32000-2 Table 115. It is the default of PDF 1.x (ISO 32000-1);
  PDF 2.0 gives it none and requires the map with a /FontFile2. Both now
  say so, and that the renderer takes a missing map as Identity too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 17:18:51 +01:00
Grzegorz MartensandClaude Opus 5.5 4d6ff291d7 M7: Reads::decode says what a stage past its limit is charged
The Claims review of the description at b7224cbc9. `Reads::decode` said a
stream past its limit is unread, not refused, "when what is left can pay
for its stages". What `DecodeBudget::decode_stages_within` does, and
`a_stage_past_its_limit_charges_what_came_before_and_the_limit` pins:
with more left than what the stages before the stopped one made and the
limit, it is charged those two; otherwise, with more left than the
stages' total (behind one filter, the limit), it is charged that total;
with no more left than that total, it spends all of it and counts as
refused.

`MAX_THAI_PROGRAMS` also names the second read of a subset's program,
for which of its glyphs have outlines, among what it pays for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 16:52:47 +01:00
Grzegorz MartensandClaude Opus 5.5 92e8e6eff0 M7: Repair Thai maps reads a /CIDToGIDMap given by reference, and takes a missing one as Identity
The Consumers review of ba28458ad..b7224cbc9:

- `recovered_thai_map` read the descendant's /CIDToGIDMap with
  `Dict::name`, on the line after e9142b03b's /Encoding check, so a map
  given by reference made Repair Thai maps pass the font over, and so
  did a missing one, which is Identity (ISO 32000-2 Table 115), as
  `composite_glyph_maps`, `LegacyThaiFont` and the renderer read it. It
  now resolves the map and takes a missing one as Identity.
- `MAX_THAI_PROGRAMS` says what it pays for: the programs, and the CMaps
  and glyph maps read to adapt their cmaps. The metrics read to find and
  measure the subsets aren't charged to it. Its refusal ("MiB of font
  data") still says what it counts.

Test: `recovered_thai_map_checks_every_kept_glyph_against_the_font_program`
recovers the same map with the font's /CIDToGIDMap given by reference and
with none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 16:48:01 +01:00
Grzegorz MartensandClaude Opus 5.5 b7224cbc9a Merge main again into fix/fonts-decode-budget
A third merge, of ba28458ad, with nothing to resolve. main's commits since
b6cbf9bb3 add no font program, metrics or CMap read in pdfcraft-fonts or
the editor's font code for this PR's allowance to cover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 16:31:54 +01:00
Grzegorz MartensandClaude Opus 5.5 e9142b03b9 M7: a composite font's /Encoding is read through a reference
The Legal forms review of 8fb534ad4. A Type 0 font's /Encoding may be
given by reference (a name or a CMap stream, either indirect), and the
metrics read resolve it: an indirect /Identity-H reads two-byte codes.
The editor's check before reading a composite font's metrics, and
`main`'s `composite_glyph_maps` and `recovered_thai_map`, read it with
`Dict::name`, which doesn't resolve: such a font counted as another
encoding, so the editor adapted no copy of it and Repair Thai maps
passed it over. All three now read it through `identity_h`, which
resolves it as the metrics do.

Tests: `a_composite_fonts_encoding_given_by_reference_is_read` (the
editor adapts a copy of a composite subset with `/Encoding 5 0 R`, object
5 `/Identity-H`, that maps 'Ω', and its metrics read two-byte codes), and
`recovered_thai_map_checks_every_kept_glyph_against_the_font_program`
recovers the same map with the font's /Encoding by reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 16:27:49 +01:00
BFlat ba28458ad8 M3: a password prompt keeps a place in the open queue for its retry (#6731) 2026-11-09 22:21:21 +07:00
BFlat 5aae0c3b82 Merge pull request #6714 from storytold/merge/ui-print-optimize2
Land print production, viewer, attachment and optimizer changes (#1918, #1998, #2539, #2348, #2701, #3806, #4139, #4378, #4401, #4763, #5259, #5508, #5316, #4434, #4440, #6092, #6098)
2026-11-09 22:20:37 +07:00
bflatastic c45ac1a873 Merge main into merge/ui-print-optimize2
# Conflicts:
#	crates/ui-egui/src/app/files.rs
#	crates/ui-egui/src/tools/forms_ui.rs
2026-11-09 22:03:08 +07:00
BFlat d2139e58ae Merge pull request #6716 from storytold/merge/xfa2
Land XFA layout, scripting and form fixes (#3589, #4336, #4359, #4517, #4537, #4608, #4662, #4664, #4784, #4791, #5010, #5122, #5124, #5153, #5225, #5368, #5369, #5370, #5393)
2026-11-09 22:02:14 +07:00
bflatastic 302b61c7e3 Fit the landed changes together: the highlight test batches value edits, since a form-script event can't be batched 2026-11-09 21:43:02 +07:00
bflatastic e52b2de518 Fit the landed changes together: the full-screen note test expects its pop-up, and the lock-question test turns the signature tool on before opening the Sign menu 2026-11-09 21:42:59 +07:00
bflatastic 36e7121ab0 Merge main into merge/ui-print-optimize2
# Conflicts:
#	crates/render/src/inspect.rs
#	crates/render/src/lib.rs
2026-11-09 21:16:23 +07:00
bflatastic 16b57d6a91 Merge main into merge/xfa2
# Conflicts:
#	crates/engine/src/tests.rs
2026-11-09 21:15:35 +07:00
BFlat fe3879bc88 Windows gate: the owner-rights test allows an elevated run to replace the list, if it then restricts the file (#6729) 2026-11-09 21:14:18 +07:00
BFlat 2c30c14e10 Merge pull request #6713 from storytold/merge/comments-annot2
Land comment groups, appearance defaults, callout parts, pop-ups and stamp lines (#4358, #4365, #4094, #2137, #2345)
2026-11-09 21:14:02 +07:00
bflatastic 62b8a2c326 Fit the landed changes together: compare the node's name as text 2026-11-09 21:13:26 +07:00
bflatastic a460022c20 Fit the landed changes together: compare an XFA node's name without a needless reference 2026-11-09 21:10:40 +07:00
bflatastic 5f860fb5ff Fit the landed changes together: the XFA split's break search gets its own function, keeping it under clippy's complexity limit 2026-11-09 21:06:46 +07:00
bflatastic f822505250 Fit the landed changes together: the rotation keys get their own function, keeping the shortcuts under clippy's complexity limit 2026-11-09 21:04:55 +07:00
bflatastic 3774c3312a Fit the landed changes together
The forms editor's test draft sets the focus's obj and list, which main added, and the focus scripts look up the focused field with focused_field so a same-name twin is read as main reads it.
2026-11-09 21:03:12 +07:00
bflatastic 2af11131ef Merge main into merge/xfa2
# Conflicts:
#	crates/automation/src/tools.rs
#	crates/automation/tests/it/automation.rs
#	crates/forms/src/tests.rs
#	crates/ui-egui/src/tools/forms_ui.rs
#	parity/acrobat-features.toml
2026-11-09 20:57:05 +07:00
bflatastic 2ce2d3cf81 Merge main into merge/ui-print-optimize2
# Conflicts:
#	crates/automation/src/redact.rs
#	crates/automation/src/tools.rs
#	crates/print/src/lib.rs
#	crates/render/src/inspect.rs
#	crates/render/src/lib.rs
2026-11-09 20:56:15 +07:00
bflatastic 856d3fb0ac Merge main into merge/comments-annot2
# Conflicts:
#	crates/render/src/inspect.rs
#	crates/render/src/lib.rs
#	crates/ui-egui/src/canvas/page_pointer/tests.rs
#	crates/ui-egui/tests/it/comments.rs
2026-11-09 20:56:09 +07:00
Grzegorz MartensandClaude Opus 5.5 8fb534ad4c M7: tests pin what a stage past its limit charges the allowance
Mutation testing of 063f8629e missed eight mutants in
`DecodeBudget::decode_stages_within`: nothing pinned what a read
stopped at a stage's limit, at the stages' total or at what is left of
the allowance charges, which limit it reports, or where one case gives
way to the next.

`a_stage_past_its_limit_charges_what_came_before_and_the_limit` reads
each way, and each boundary on both sides: a byte more room than the hex
stage and the limit (charged them, a byte left), exactly that (past the
allowance: all of it spent, its limit reported), a hex stage making
exactly the limit so the Flate stage reaches the stages' total with more
left (charged the total), with exactly the total left of a larger
allowance and with less (spent, the allowance's limit reported), and,
with nothing left, a stream with data that decodes to nothing (refused)
and an empty one (read).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 14:08:29 +01:00
Grzegorz MartensandClaude Opus 5.5 509cf9f9fd M7: the editor reads no metrics for a composite font it can't adapt
The Resources review of 8eead8ecd:

- `embedded_sfnt_within` read a composite font's metrics within the
  editor's allowance before `composite_glyph_maps` refused any `/Encoding`
  but Identity-H, so an Identity-V or UniJIS font spent its ToUnicode and
  `/CIDToGIDMap` for nothing. Its `/Encoding` is now checked first; a
  program with a cmap of its own is kept as it is, as when the rebuild
  finds nothing.
- The Thai edit's allowance, `MAX_THAI_PROGRAMS`, now also pays for the
  CMaps and glyph maps its subsets' metrics and codes are read from: its
  doc and its refusal say "font data", not "font programs".
- `a_symbolic_truetype_font_without_an_encoding_reads_its_glyph_names`
  also reads its font, whose `post` table names its glyphs, within an
  allowance: the program is paid for by the glyph-name reading, by the
  Mac OS Roman one and for the widths, and a byte short of the first the
  names go unread.

Tests: `a_composite_font_that_cant_be_adapted_spends_only_its_program`,
and `adapting_a_programs_cmap_is_paid_for` again expects a composite font
whose codes aren't Identity-H to pay for its program only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 13:36:44 +01:00
Grzegorz MartensandClaude Opus 5.5 063f8629e4 M7: each filter stage of a font's stream is held to its limit, as without an allowance
The Resources review of 8eead8ecd. The program read and the metrics reads
passed their limit times the number of filter stages as one total, which
any one stage could use: a FontFile2 of Flate making 128 MiB of
whitespace, under seven ASCIIHex stages (a 130 KB file), made all of it
before being refused, about 0.1 s a font where `main`'s bound stopped it
at 16 MiB. `edit`'s `matching_thai_outline` reads such a program for each
of a page's fonts with no total, and every `Metrics::from_dict` up to
three times.

Now each stage is held to the limit, as a decode without an allowance
holds it, and the stages together to the limit for each of them, so every
stream the stage scaling was for still reads: a full `/CIDToGIDMap` or a
12 MiB program behind ASCIIHex and Flate. The two bounds take an entry
point of their own: `pdfcraft_filters::decode_tolerant_counted_each`,
which `run` already supports, and `DecodeBudget::decode_stages_within`,
which `Reads::decode` and the editor's program read use. A stage past the
limit is charged what the stages before it decoded and the limit; past
the stages' total, or past what is left, it is charged as
`decode_within` charges. The program, the last stage, is so within the
limit without a check of its own.

Tests: `each_stage_is_held_to_its_own_bound_within_the_total` in
`pdfcraft-filters` (a Flate stage stopped at its bound hands the next
stage nothing), `each_stage_is_held_to_the_limit` in `pdfcraft-cos`, and
in `pdfcraft-fonts` `an_editors_program_read_holds_each_stage_to_its_limit`
and `each_stage_of_a_metrics_stream_is_held_to_its_limit`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 13:36:29 +01:00
Grzegorz MartensandClaude Opus 5.5 d4abe80671 Merge main again into fix/fonts-decode-budget
main's #3962 (symbolic TrueType glyph names, 63a6f8ace and nearby) reads a
symbolic TrueType font's codes by the `post` names of the glyphs its cmap
selects, before the Mac OS Roman reading:

- crates/fonts/src/pdf.rs, `unicode_map`: both readings stay, in main's
  order, each given the metrics read's allowance. The program read main
  added (`symbolic_glyph_texts`) goes through `Reads::decode`, as
  `symbolic_mac_roman_texts` does since the last merge: paid for from the
  allowance, `past_allowance` set when it can't be, behind several
  filters within the stream's own limit.
- crates/fonts/src/pdf/tests.rs: both sides added tests at the same place;
  all are kept.

Tests: `adapting_a_programs_cmap_is_paid_for` now expects the program to
be paid for a fourth time, and `the_symbolic_mac_roman_reading_is_paid_for`
becomes `the_symbolic_readings_of_a_program_are_paid_for`, paying for both
readings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 13:06:32 +01:00
bflatastic fb387d95d9 Fit the landed changes together: the lock-question test opens the Sign menu by its current label 2026-11-09 18:54:56 +07:00
bflatastic 99eaa73ca8 Fit the landed changes together: a double-click on a group member opens the group's pop-up 2026-11-09 18:54:18 +07:00
bflatastic 28585c01a5 Fit the landed changes together
- engine: the on_changes test's XfaRun literals set highlight; the script
  runner reads its template before borrowing the live form for to_js; has_event
  reads the paged live form, as the rest of the runner does, so focus scripts
  find a page-master copy by its own SOM path.
- js: property reads compare and copy the shared (Arc<str>) node text, and the
  property tests build XHost with its current signature.
- xfa: the Emitter test literal sets groups and used; a field split that is
  undone also restores the names handed out; rustfmt on a merged import.
- parity: drop the use/usehref gap from form.xfa-dynamic, as prototypes are now
  resolved before the template is parsed.
2026-11-09 18:46:50 +07:00
BFlat b6cbf9bb30 Merge pull request #6710 from storytold/merge/forms-links-text2
Land forms, links, find and redaction-save fixes (#4496, #4532, #3567, #3773, #4470, #4474, #4497, #5658)
2026-11-09 19:45:39 +08:00
BFlat 6b56de9ecf Merge pull request #6711 from storytold/merge/render-patches2
Land renderer patches, layer auto state, auto-tag artifacts, export clips and print page lookup (#3969, #4393, #4396, #4410, #4433, #4459, #4159, #5075, #5973)
2026-11-09 19:45:24 +08:00
BFlat 9f4191336d xtask: the private temp folder builder is mutable only on Unix (#6712) 2026-11-09 19:45:13 +08:00
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 9147411e9c Merge #5370: M12: a flowed XFA subform in a table cell is as tall as its content (#5367)
# Conflicts:
#	crates/xfa/src/layout/measure.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:40:22 +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 74b3452328 Fit the landed changes together: the print-settings discard is its own function 2026-11-09 18:37:28 +07:00
bflatastic c7f1fc7540 Merge #5225: M12: XFA field names made up for repeated fields never take a name the template uses (#5224)
# Conflicts:
#	crates/xfa/src/layout/common.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:36:41 +07:00
bflatastic 0575de68ff Merge #5153: M12: an XFA subform without a height is as tall as its content, flowed or positioned (#5152)
# Conflicts:
#	crates/xfa/src/tests.rs
2026-11-09 18:36:01 +07:00
bflatastic 629d211506 Merge #5124: M12: XFA table rows start at their minimum height, and a growable row group splits at a page break (form.xfa-dynamic)
# Conflicts:
#	crates/xfa/src/layout/flow.rs
#	crates/xfa/src/layout/measure.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:35:21 +07:00
bflatastic 8c19e0d1bd Merge #5122: fix(xfa): AcroForm field walks read each field once and bound what they queue
# Conflicts:
#	crates/xfa/src/pdf.rs
#	crates/xfa/src/tests.rs
2026-11-09 18:33:53 +07:00
bflatastic 85ebbab0ce Fit the landed changes together: modify.rs imports polygon_rect 2026-11-09 18:33:09 +07:00
bflatastic d92046f815 Fit the landed changes together
Format the attachment-key numbering kept from both sides of #5508's merge.
2026-11-09 18:32:42 +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 c994a0a4ac Fit the landed changes together
Comment literals in tests gain the callout text box and pop-up fields, and the review-state map is formatted.
2026-11-09 18:29:25 +07:00
bflatastic 74cc90dc50 Merge #6098: fix(forms): AFSimple_Calculate folds field values as it reads them, within an allowance
# Conflicts:
#	crates/forms/src/af/tests.rs
2026-11-09 18:28:11 +07:00
bflatastic 28247e037f Merge #6092: fix(a11y): annotation checks and form tagging stop copying shared annotation data
# Conflicts:
#	crates/a11y/src/tests.rs
2026-11-09 18:27:44 +07:00