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
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
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
main's Thai work (#6249, #3492) and the Type 3 and symbolic TrueType
metrics changed the code this PR bounds. Each conflict, and each read main
added that this PR's allowance must cover:
- crates/fonts/src/pdf.rs, the editor's program read: this PR's read is
kept, its stage-scaled limit and its check for an allowance already
spent; main's read had /DecodeParms by reference too, which it has.
- The editor's loop: main rebuilds a cmap for a composite subset without
one from its ToUnicode and /CIDToGIDMap, where this PR skipped Type 0.
main's composite support stays, with the metrics read within the
editor's allowance (`from_dict_within`) and no copy adapted from metrics
read past it, as for a simple font. The /CIDToGIDMap the cmap is rebuilt
from (`composite_glyph_maps`) reads as the font's other streams do
(`Reads::decode`): within the allowance, behind several filters too.
- Reads main added to the metrics read: a Type 3 font's glyph
descriptions (`type3_glyph_box`), and the program read that tells a
symbolic TrueType font's codes as Mac OS Roman
(`symbolic_mac_roman_texts`). Both are now paid for from the allowance
the metrics are read within, and a refusal sets `past_allowance`. The
glyph descriptions keep their own limits (64 KiB each, 4 MiB a font).
- Constants and helpers both sides added beside one another, and the
tests both appended, are all kept. Callers main added of
`unicode_map` pass a reading without a total, as `from_dict` does.
- crates/edit/src/rules.rs: the refusal of a page whose content didn't
all decode stays, and the operators come from main's `text::joined`.
Tests: `adapting_a_programs_cmap_is_paid_for` now expects a composite
font's ToUnicode, and a simple font's program read again for the Mac OS
Roman reading, to be paid for. Four are new:
`adapting_a_composite_subsets_cmap_is_paid_for`,
`a_full_cid_map_rebuilds_a_composite_subsets_cmap_behind_two_filters`,
`type3_glyph_boxes_are_paid_for_from_the_allowance` and
`the_symbolic_mac_roman_reading_is_paid_for`. The fixture of main's test
of a composite subset without a cmap moves into `cmapless_composite`,
which both use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY