* M10 #5130: resolve Windows printer-default duplex before spooling
* M10 #5130: document printer duplex resolution
* M10 #5130: adopt DocumentDuplex driver fix from craft-print
* M10 #5130: adopt two-sided capability selection fix
* M10 #5130: update documented craft-print revision
* M10 #5130: fall back when Windows duplex defaults cannot be read
* fix(print): satisfy duplex review formatting and refresh shared pin
* Repin craft-print to craft-libs main (#411 and #133 merged); ask only the picked printer's driver for two-sided support
craft-libs b6b8a55b has #411 (DocumentDuplex) and #133 (two_sided(name)). The
Printer default support check now reads that one driver instead of listing
every printer's capabilities at submit time (#2192).
---------
Co-authored-by: robertobulltech <341520377+robertobulltech@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
* Print: list printers without asking drivers; ask only about the one picked (#2192)
On Windows, craft-print's printers() reads every driver's capabilities to
report two-sided support. Windows takes about 48 s to give up on a printer
it can't reach, such as a WSD printer on another network, so the Print
dialog waited for each one before it listed anything (#2192).
The dialog now lists printers from craft-print's printer_queues(), which
asks no driver, and asks two_sided() about the printer the user picks, on
a background thread (PdfCraftApp::two_sided_source, set by the desktop app
like printer_source). Each answer is kept for the session, so a printer is
asked about at most once, and one nobody picks is never waited on. The
"doesn't take two-sided settings" note shows once the answer arrives.
The automation printers tool still uses printers(), so it keeps reporting
two_sided.
Needs the craft-libs change that adds craft_print::two_sided.
* Build: take craft-print from storytold/craft-libs#133
Pins craft-print to the head of storytold/craft-libs#133 (b6a3b87), which
adds craft_print::two_sided as one commit on craft-libs main at 5ee809f.
craft-print is otherwise unchanged from 7ee35a9, the rev main pins now; the
other craft-libs crates stay where they are. Fetched through the
storytold/craft-libs URL, so cargo-deny's allow-git is unchanged. To be
replaced by the merge commit once #133 lands.
* Print: run at most four two-sided reads at once
A driver Windows can't reach holds its two-sided read for about 48 s, so
picking printer after printer started a thread for each. At most four now
run at once; past that, the printer picked is asked once a read finishes.
Tests: hanging reads are capped and the picked printer is asked after one
finishes; a list that already says whether a driver takes two-sided
settings is used as it is, with no driver asked.
* Repin craft-print to craft-libs main, where #133 merged (b6b8a55b)
---------
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
* fix: cap print dialog height to the screen so it can't overflow top/bottom
The Print dialog modal had no height constraint, so on screens where the settings+preview content (especially with the driver's Properties... panel expanded) exceeds available screen height, the dialog overflowed equally off the top and bottom, making Print/Cancel unreachable. Wrap the settings+preview area in a ScrollArea capped to the viewport's available height (read through viewport_rect(), which holds steady across frames unlike raw screen_rect), keeping the title and button row outside so they stay reachable. Includes a kittest regression test.
* Print dialog: size the scroll area from its cap, not from what the modal offers this frame
Opened after a taller dialog, the shared modal remeasures from a height of zero (#5118) and
offers less, so the settings area took the previous dialog's leftover height: Print was 408 pt
after Preferences against 674 pt fresh (dialog_chrome::a_dialog_keeps_its_own_height_after_a_taller_one).
min_scrolled_height(max_h) makes the viewport the cap; auto_shrink still fits shorter contents.
Comments cite #4252 and #5317.
---------
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
* fix(print): M10 — Windows ya no cambia las letras de las fuentes TrueType incrustadas (#3132)
Un subconjunto TrueType incrustado numera sus glifos de nuevo (en el
subconjunto de Arial de ReportLab el espacio es el glifo 11; en la Arial
instalada, el 3). Al imprimir en Windows, Windows.Data.Pdf → Direct2D →
XPS conservaba esos números de glifo pero los dibujaba con la fuente
instalada del mismo nombre: «Revisión» salía como «Wj{nxn» y la «z» en
negrita como «Ä», tanto en las colas Brother como en la impresora Adobe PDF.
La copia que va a la cola de Windows renombra ahora cada programa
TrueType/OpenType incrustado como PdfCraftPrint<n>, en su tabla name
y en el PDF (/BaseFont, /FontName; el subconjunto conserva su etiqueta),
para que ninguna fuente instalada responda a ese nombre. Glifos,
codificaciones y /ToUnicode no cambian. CUPS y Guardar como PDF reciben
el PDF tal cual.
Tests: extremo a extremo con impose (la página se renderiza idéntica
antes y después, y el test falla sin el arreglo), fuentes que comparten
programa, Type 0, reescritura por plataforma de la tabla name y programas
dañados sin pánico. Falta comprobarlo en una impresora física con Windows.
Fixes#3132
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(print): M10 — solo se renombran en el PDF las fuentes cuyo programa se reescribe (#3132)
Si PdfCraft no puede leer o reescribir un programa incrustado (no es un
sfnt, está dañado o pasa de 64 MiB), ahora conserva también su /BaseFont
y su /FontName. Un visor que tampoco pueda cargarlo seguirá eligiendo el
sustituto por el nombre original («Arial») en vez de por PdfCraftPrint<n>.
Si no queda ningún programa reescrito, el PDF va a la cola tal cual.
Lo señaló CodeRabbit en el PR #4098. Test nuevo:
a_program_that_cannot_be_rewritten_keeps_its_name_in_the_pdf_too (falla
con el comportamiento anterior).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(print): M10 — el renombrado de fuentes para Windows decodifica contra un presupuesto total (#3132)
Cada programa ya tenía un tope de 64 MiB, pero un PDF con muchos
programas distintos podía sumar mucho trabajo de descompresión al
imprimir. Ahora todo el renombrado comparte un DecodeBudget de 512 MiB,
el mecanismo que main acaba de introducir en print. Agotado el
presupuesto, los programas que faltan conservan su nombre, igual que uno
ilegible.
Test nuevo: programs_past_the_shared_decode_budget_keep_their_names
(falla si se decodifica sin presupuesto).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(print): M10 — también se renombran los CIDFont escritos dentro de su fuente Type 0 (#3132)
Si /DescendantFonts lleva el CIDFont como diccionario en línea (en el
propio Type 0 o en un objeto array aparte), el recorrido no llegaba a
él. Cuando compartía programa con otra fuente, el programa se renombraba,
pero el Type 0, el CIDFont y su descriptor seguían diciendo «Arial», y
Windows podía volver a cruzarlos con la Arial instalada. Ahora el Type 0
se trata en el mismo recorrido, sin el segundo que hacía type0_names: un
CIDFont en línea apunta su programa, y al renombrar se cambian el Type 0,
el CIDFont y su descriptor en línea.
Lo señaló CodeRabbit en el PR #4098. Test nuevo:
a_cidfont_written_inside_its_type0_font_is_renamed_with_it (falla sin el
arreglo).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(print): M10 — renamed_table ya no copia cada cadena de la tabla name (#3132)
renamed_table copiaba el texto de cada registro antes de comprobar el
tamaño del resultado: una tabla name de menos de 1 MB con 65.535
registros apuntando a una misma cadena de 65.535 bytes reservaba unos
4 GiB, y un fallo de reserva cierra el proceso al imprimir en Windows.
Ahora la tabla se lee entera sin copiar ningún texto (cada registro
guarda el offset y la longitud de origen) y después cada cadena distinta
se guarda una sola vez: los registros que compartían cadena la siguen
compartiendo y el almacén nunca pasa de lo que alcanzan los offsets de
16 bits. Una tabla que declara más registros de los que caben antes de
sus cadenas se rechaza de entrada.
Lo encontró RomanLis91 en el PR #4098. Tests nuevos:
records_that_share_one_string_keep_sharing_it (falla con el código
anterior) y a_name_table_claiming_more_records_than_fit_before_its_strings_is_refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A FormCalc script under a time limit is watched twice: the caller waits
`timeout` for it, and the interpreter reads its own deadline, set to the
same `timeout`, every 256 steps. Which one notices first is down to the
scheduler, and the two reported different things: the caller's outcome is
abandoned with no changes (the form's scripts go off), while the
interpreter's kept every change the script made before the deadline.
For a click script that loops on addInstance, that difference is 1000 rows
(the most one run may add). Since repeating subforms lay out up to 1000
rows instead of 50 (#4608, landed with #6716), keeping them means laying
out a 1000-row form again, twice, which takes most of a minute in the test
build. The engine test hostile_formcalc_scripts_cannot_hang_or_bloat_the_document
took 3.0 s when the caller noticed first and 46-61 s when the script did,
so it failed its 60 s bound at random.
Now a run stopped by its own clock reports what an abandoned run does: the
same error, no changes, abandoned. XfaSession treats that outcome like a
wait that ran out (the session is dead after it). Limits other than time
(steps, loops, depth) keep their changes as before.
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
After a print to a printer, its paper, page sizing (Fit, Actual size,
Shrink, Custom %) and two-sided choice are remembered for that printer and
brought back when it is chosen. The printer printed to last is preselected
after a restart while it is listed, else the system default. Saved as
print_printers, most recent first, at most 32; malformed entries are skipped.
* M10: grayscale with Print as image prints on a driver that keeps no grayscale (#5806)
With Print as image and grayscale, PdfCraft renders the pages as grey
pictures itself, yet the job still asked the driver for grayscale. A
driver that keeps no grayscale setting, Microsoft Print to PDF among
them, made craft-print refuse the job ("the printer's driver did not
retain the requested grayscale; nothing was sent"), so even the grey
pictures never printed. The job now asks the driver for grayscale only
when the pages aren't grey pictures already
(print::driver_needs_grayscale), in the dialog and in doc_print.
The print.grayscale parity note and the doc_print as_image description
say so, and the note names what is still missing: vector pages to such
a driver are refused rather than printed in colour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M10: the grey-pictures test checks that the pictures are grey too (#5806)
The test checked that grey pictures don't ask the driver for grayscale,
but not that the pictures are grey. A change that dropped grayscale from
the dialog's raster settings, while the job still skipped the driver,
would have printed in colour with the test passing. It now asserts both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: austin62jake <340374338+austin62jake@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
* fix(print): fall back to the lpoptions listing when the PPD is unreadable
cupsd keeps /etc/cups/ppd/<queue>.ppd at 0640 root:cups on Arch and
similar setups, so an ordinary desktop user cannot open it. The option
reader treated that permission error as "no options", and Print ▸
Properties… said "This printer offers no settings of its own" for a
queue whose driver options cupsd was serving all along through
lpoptions.
When the PPD is missing, unreadable, or parses to no options, the list is
now built from `lpoptions -p <queue> -l`. The options Craft handles
itself are skipped, as the PPD parser does, and the starred choice is the
fallback default.
Fixes#4699
* fix(print): skip CUPS custom-value templates in the lpoptions fallback
`lpoptions -l` prints `Custom`, `Custom.<TYPE>` (`Custom.PASSCODE`,
`Custom.STRING`, …) or `Custom.WIDTHxHEIGHT` in place of the choices of an
option whose value the user has to type. Nothing in the dialog collects that
value, so offering the template sent its literal text to the driver. Those
entries are now dropped, and a starred template never becomes the default.
The choice cap is also tested before the duplicate scan, so a listing past
`MAX_LPOPTIONS_CHOICES` no longer walks the stored choices on every token.
Refs #4699
* fix(print): refuse a destination that starts with a dash
`lpoptions` parses its argv with getopt, so `-p -E -l` reads `-E` as an
option rather than as the destination; `--` cannot help, because it ends
option parsing before the `-p` that names the queue. Reject a leading
`-` on the queue and on the instance component beside the existing name
checks, before the PPD is opened or the command is spawned.
* fix(print): read the listing by its last colon, and stop inventing a default
`list_group()` prints the option as `"%s/%s:"` from the PPD's keyword and
translated text and each choice as a bare `" %s"`, so a colon can sit in
the text but not in a choice keyword. Splitting at the first colon turned
the rest of the text ("Supporto: Tipo") into invented choices; split at
the last one instead.
When the choice CUPS marks is a custom-value template, the parser dropped
it and stood the first retained choice in as the default. The UI then
showed a value the job does not use, because only options changed away
from the default are submitted. Leave the default empty in that case:
there is nothing the dialog could collect, and an invented choice is
worse than no selection.
---------
Co-authored-by: Jarvis (Hermes) <jarvis@localhost>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Add Folio (8.5 x 13 in / 612 x 936 pt) and F4 (210 x 330 mm / 595.28 x 935.43 pt)
to the PAPERS preset table and map them to standard PWG 5101.1 media names
(na_foolscap_8.5x13in and om_folio_210x330mm) in cups_media.
Closes#4777.
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
* M10: the Print dialog remembers two-sided, grayscale, collate, sizing and comments & forms (#2001)
Persist the choices that describe how this computer prints and restore
them on the next launch, under the names doc_print takes; copies, pages,
layout, orientation and the printer still start fresh for each job.
Those names now live in one place, pdfcraft_print::names, used by the
tool and the settings file. Settings are untrusted: an unknown name or a
custom scale outside the dialog's 1..=1000 % keeps the default.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* M10: the Print dialog also remembers a booklet's binding side (#2707)
Acrobat keeps the booklet binding (left or right) between prints;
PdfCraft started at Left every time. It is now kept with the other
remembered print choices, named as doc_print names it, and an unknown
saved value keeps the default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M10: Print starts each job's copies, pages, layout and orientation afresh (#2001)
CodeRabbit on #2103: the README promised that copies, pages, layout and orientation
start fresh, but open_print carried them over through `..keep` within a session.
ISO 32000-2 Table 147 initialises these per document when the print dialog opens
(NumCopies "typically 1", PrintPageRange), so each opening now resets them to the
defaults (Selected pages still win when thumbnails are picked); two-sided,
grayscale, collate, sizing, Comments & forms, binding, the paper and the printer
picked last carry over as before. Two-sided names now include "printer" (leave it to
the printer, main's default since #2422), so that choice is remembered too.
main's paper-persistence test expected grayscale not to persist; it is one of the
remembered preferences.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M10: a new print job also resets its layout's own options and leaves Label (#2001)
CodeRabbit on #2103: resetting the layout to Size kept pages per sheet, order, borders,
booklet subset and the poster options through `..keep`, so choosing Multiple again
brought back, say, a cut-and-stack order; and a job printed on Label went back to Size
without giving back the paper Label replaced. open_print now leaves Label through
set_handling (#3564) and resets the layout's own options with the rest of the job's
choices; the binding and sizing stay remembered. The README now says what resets on
each opening and what carries over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M10: the Print dialog also remembers Print as image and its resolution (#6392)
A printer that needs pictures keeps getting them after a restart: as_image and
dpi are saved with the other print preferences and restored, a resolution the
dialog doesn't offer keeping the default 300 dpi. The bad-settings checks move
to their own test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: mtlcl410 <chuanlin@niar.org.tw>
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
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
Editor, export and redaction must not read or rewrite text that the page view and Find never see, which a mismatched cap allows on large streams.
Lower MAX_DECODED to 256 MiB to match the renderer cap, so every Stream::decoded-based content reader stops at the same byte and oversized streams error instead of exposing later text.
Test: decoded_stops_at_renderer_cap_of_256_mib failed before the change and passes after.
Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com>
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
Since 0a00862e9, what an image is stored again with (its size, filters and parameters) is
remembered by number and generation, so a value reached through a reference of another
generation is fetched again rather than taken from what the reference of its object's own
generation read. The README's list of what may be read again now says so. What an image names
is still remembered by object number, so a dictionary named that way isn't read again.
The same sentence said a chain of more than 32 references may be read again. `names_in` follows
33 references before it gives up on a chain, as `a_chain_of_references_is_named_to_the_limit`
says, so it is more than 33.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
The Consumers review of 64dfe1fd4. The README said a surviving form loses
the resource entries for removed XObjects its own content does not draw.
It also keeps those a form without resources of its own, drawn through
them, still draws, and since this PR a redacted page keeps them the same
way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
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
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
One conflict, in redact lib.rs: the module list, where main added `elsewhere` and
`identification` (#6714) and this branch `globals`. All three stay, in order.
Main's #2448 work also added a sixth step to `apply`, after the verification and the pattern
guard: `metadata::scrub` removes the `/Info` entries and the catalog's and pages' XMP that repeat
the removed text. An image that only such a packet refers to leaves the file then, after
`unshare` had looked for what left, so its shared dictionary stayed. `unshare` now runs after
the scrub, last of what can take something out. Storing images again changes no page's content
and no pattern cell, so the verification and the pattern guard before it check the same pages
they did. The crate's doc says the dictionary goes last. Remove Hidden Information's order is
unchanged: main's changes to `sanitize.rs` only read (`owned_values`).
Test: `an_image_only_the_scrubbed_metadata_refers_to_takes_a_shared_jbig2_dictionary_too`. An
image under the mark is also named by the catalog's XMP, which repeats the text removed beside
it. After the redaction the packet is gone, the image on the other page is stored again, and
the dictionary is gone too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
A third merge of main, at ba28458ad. Main's #2448 (in #6714) collects
the text a redaction removes from the walk's scope, which this branch
also reads after the walk for what it left of the page's allowance.
- `interp.rs`: `Scope` keeps this branch's `into_budget` and gains
main's `with_removed_room`, `flush` and `removed`.
- `lib.rs`: the page's walk starts with main's room for removed text,
and after it the scope's removed text is collected as on main, then
what it left of the page's allowance is taken for the cleanups, last,
as taking it consumes the scope.
Main's new code reads no content stream or form in the walk or the
cleanups, so nothing of this branch's allowance or one-operator reads
needed carrying into it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
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