Commit Graph
238 Commits
Author SHA1 Message Date
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 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
bflatastic 117f3b1102 Merge #4497: M8: after Apply redactions, Save asks where and proposes the name with _Redacted (protect.redact-filename-suffix) 2026-11-09 18:21:20 +07:00
bflatastic 5af131db8e Merge #4358: M5: comments in an /RT /Group are one comment, with the primary's attributes (#4357)
# Conflicts:
#	crates/automation/src/comments.rs
#	crates/automation/tests/it/automation.rs
#	crates/render/src/inspect.rs
#	crates/render/src/inspect/annotations.rs
#	crates/render/src/lib.rs
#	crates/ui-egui/src/panels/comments_panel.rs
2026-11-09 18:17:31 +07:00
mtlcl410 ed6c8afdef Merge remote-tracking branch 'upstream/main' into lane3-2539b 2026-11-09 18:49:37 +08:00
Dylan M. Taylor 4cdc3d15dd fix(redact): read the page tree once per sanitize pass, not per page (#5983) 2026-11-09 18:37:46 +08:00
Dylan M. Taylor ca56433f5f fix(redact): read text-state operands from the end, as the renderer does (#5951) 2026-11-09 18:36:34 +08:00
Dylan M. Taylor 743a772ee9 fix(content): an inline image only the renderer reads counts as ambiguous (#5874) 2026-11-09 18:36:04 +08:00
dotCooCoo 2d29f7c370 Merge main into feat/redacted-file-name
# Conflicts:
#	crates/automation/src/redact.rs
2026-11-09 02:27:02 -08:00
mtlcl410 bd74ecab92 Merge remote-tracking branch 'upstream/main' into lane3-2539b
# Conflicts:
#	crates/redact/src/tests.rs
2026-11-09 18:20:12 +08:00
ljkx 930b441312 fix(redact): Remove Hidden Information reads only what the chosen categories need (part of #5873) (#6021) 2026-11-09 18:01:36 +08:00
mtlcl410andClaude Opus 5.5 cc1de556be Merge upstream/main into feat/comment-groups
Main's Replace Text (#5689) makes the caret the group's primary, as this
branch does. Kept: this branch's member-by-member deletion and release of
orphaned subordinates, with main's seed() (the strikeout also seeds its
caret) and its pair restyle; the writer returns the caret (the group's
comment) and leaves the strikeout without /Contents. Main's tests read the
strikeout from the caret's group instead of its replies, and the automation
test reads the strikeout's own colour from an XFDF export, since
comment_list gives a group its primary's attributes only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:57:18 +08:00
mtlcl410andClaude Opus 5.5 8c142bef2e Merge upstream/main into fix/redact-metadata-2448
Keeps main's recovery renewal after applying redactions alongside the
combined notification, and reads XMP and rich-text streams with their
/DecodeParms given by reference made direct, as main's stream readers do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:12:29 +08:00
bflatastic 12dfc73cfd Format the renamed helper's call 2026-11-09 09:03:50 +07:00
bflatastic fb9926858a Name the content-draws helper apart from the undrawn-image one in redaction pruning 2026-11-09 09:02:23 +07:00
dotCooCoo b2988cb8e1 Merge main into feat/redacted-file-name 2026-11-08 17:25:08 -08:00
bflatastic 52f31397be Merge #5969: fix(redact): read inherited forms once for all names in resource cleanup
# Conflicts:
#	crates/redact/src/tests/surviving_references.rs
2026-11-09 08:06:41 +07:00
bflatastic a97b83fc83 Read undrawn-image streams with their /DecodeParms by reference 2026-11-09 08:06:03 +07:00
bflatastic b6ec3fe38d Merge #6474: Type 3 pruning counts a page's appearances toward one operator total
# Conflicts:
#	crates/redact/src/tests/surviving_references.rs
2026-11-09 08:05:56 +07:00
bflatastic 34a953a72a Merge #6460: Hidden layers includes layers a View usage rule turns off
# Conflicts:
#	crates/redact/src/tests.rs
2026-11-09 08:03:18 +07:00
bflatastic 4dd3e2062f Merge #6459: Removed layers leave every optional-content configuration
# Conflicts:
#	crates/redact/src/tests.rs
2026-11-09 08:02:50 +07:00
bflatastic 2e380a42b5 Merge #6458: Remove Hidden Information unlinks images nothing draws
# Conflicts:
#	crates/redact/src/tests.rs
2026-11-09 08:02:23 +07:00
oracola91 34f9bea0ad Private data takes OPI links and alternate images (#6457) 2026-11-09 08:51:40 +08:00
oracola91 d0083c09a4 Links, actions and JavaScripts takes the scripts out of bookmarks (#6456) 2026-11-09 08:51:25 +08:00
Dylan M. Taylor a55eaa90e3 fix(redact): prune the resources of Forms a surviving Type 3 glyph draws (#5924) 2026-11-09 08:40:35 +08:00
mtlcl410 ae7eba8df4 Merge remote-tracking branch 'upstream/main' into lane1-2539
# Conflicts:
#	crates/engine/src/edit/run.rs
2026-11-09 02:21:08 +08:00
FredApps 8d4f2db5b1 Merge branch 'main' into fix/redact-nan-quads 2026-11-08 17:51:37 +01:00
Dylan M. Taylor da5a664276 review: keep reading queued forms after one that will not decode 2026-11-08 11:14:23 -05:00
mtlcl410andClaude Opus 5.5 a5306022d8 Merge upstream/main into feat/comment-groups
Conflict: crates/automation/tests/it/automation.rs (kept the group test and main's ungroup tests).
Also carries the one-line fix for main's crates/edit/src/rules.rs (content_streams() now returns
ContentStream): page_rules joins its streams with crate::text::joined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 22:54:03 +08:00
mtlcl410andClaude Opus 5.5 27614e43d8 Merge upstream/main into fix/redact-metadata-2448
Resolve the conflicts in crates/engine/src/lib.rs (keep both the
redaction-identification and user-regex re-exports) and Cargo.lock
(main's, plus the redact dependencies). Drop the branch's copy of the
test helper pdf_of, which main now defines identically. Fix main's
pdfcraft-edit build: rules.rs joins its content streams through
crate::text::joined now that content_streams returns ContentStream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 22:54:03 +08:00
FredApps 5e0619d7eb Merge pull request #1977 from vangel76/feat/redact-mark-regex
Redact: mark text by caller-supplied regex (#1886)
2026-11-08 13:30:34 +01:00
FredApps 8287ad0d06 Merge pull request #6289 from Piohrun/fix/partial-content-rewrite
M2.6: never rewrite a content stream from the part before its damage
2026-11-08 13:10:56 +01:00
Chiranjeevi U adbf1dc01f M9: signed appearances show their real validation state (#79) (#2808)
* M9: redraw named-layer signature status layers

- /N drawing one Form that holds n1 and n3: n1, n3 and n4 redrawn for the state
- copies made once per original and state; originals never edited
- Lucide icons; captions in Helvetica, embedded Inter, or craft-fonts outlines

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: draw signature states in the display copy, exports and print

- marks follow the bytes shown; a changed verdict redraws after a metadata edit
- background opens pick up the trust and captions current at commit

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: status captions in the interface language

- the three captions in all 22 catalogues
- Signatures panel colours from sign::status_rgb

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: signature badge in the widget's corner

- a quarter of the shorter side, 48pt cap; outline only under 32pt
- dropped for a drawn valid state; bottom-right where the appearance draws one
- sign_list reports verdict_shown; parity and roadmap

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: keep Arabic signature captions in logical order

Pass raw RTL catalogue text to the signature appearance shaper so UAX #9 is applied exactly once. This keeps the rendered word order and exported /ActualText consistent.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M2: read /ActualText over painted paths and pictures

- A replacement that marks no glyph reads where it paints, cut to what is visible and along the way it is drawn.
- A span the page content leaves open ends before the annotations.
- OCR and scan field detection still count such a page as having no text of its own.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M8: redaction strips /ActualText around paint it clips

- A path or shading written again under a clip counts as changed, so a partly redacted word drawn as outlines leaves no name in the file.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: recognise only layer forms that draw n1 and n3

- Settle on the one form holding both first, then read its content once per call.
- Skip an /N that is its own layer form; copies keep the layer's /OC.
- A verdict counts as drawn when any visible widget shows it.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: keep the signature badge and redraw wherever a state changes

- The badge stays for every state; its mark sits bottom-right as the widget lies on the page.
- An edit that changes a verdict redraws every page; captions apply before startup files open.
- Print, the CLI's render and the repair log follow the display copy.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: restore the attribution usage the rebase dropped

- Usage lines for the three icons, Inter and the craft-fonts input; ATTRIBUTION.md rebuilt from them.
- Roadmap and localization notes for this round.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: pin signature field names on a nested form

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: bound the signature field walk

- kids share their parent's name and type instead of each copying them
- a widget stays listed under every field that claims it, as one shared copy
- a field written in place in a shared /Kids array is walked once
- caps of 20,000 fields, 100,000 entries and 4096-byte names; no states drawn past them

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: keep a document signed when the field walk stops early

- list_cached adds a signed row of unknown validity for the fields the walk didn't read
- Save As and actions then append, and Optimize, Sanitize and full rewrites are refused, as for any signed file

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M8: removing hidden content no longer strips the names of shadings

- With no regions to clip, a shading is left alone instead of wrapped and marked clipped
- clean_replacements skips scopes already marked, so deep nesting stays linear

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M2: read a picture's /ActualText over the whole picture

- draw_image's transform maps pixels, not a unit square, so the text sat on one pixel in the corner; the test now draws a 2x2 picture
- Painted grows in place instead of being copied through an Option

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: cap the bytes of names the signature field walk keeps

- A kid's name repeats its parent's, so one long name over 20,000 kids came to 80 MB; the walk now stops at 4 MiB of names and counts the rest as unread
- Cover the widget written into a shared /Kids array and the field cap among kids, which no test caught
- sig_fields is private again, and the cap docs say what happens past each cap

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: skip status-layer work that changes nothing; fix tests that could not fail

- show_status_layers and has_status_layers return before reading the form when nothing is signed, so an unsigned form no longer walks every field on each edit
- revalidate and draw_captions leave documents with no drawn state alone, and no longer copy the signature list to update it
- Scan form detection reads the page's text once; comment and badge tips share one hover rule
- Tests: drop a table row that repeated another, stop taking an expectation from the code under test, stop comparing an Arc with itself, give bare asserts messages

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: test the signature field name cap at the engine boundary

- A 4,096-byte name is read and blocks a full rewrite; at 4,097 bytes the list holds the unread-fields row and a full rewrite is still refused

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: test signature states beside fields the viewer draws

- One display copy holds both a field's drawn appearance and the signature's state.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: draw Thai and Devanagari status captions in their own script

- script_of names Thai and Devanagari, so a craft-fonts face meant for either draws the caption as outlines; without one it stays English.
- Localization, parity and the craft-fonts usage line list the new scripts.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M2: paint left out by an exclusion mixes its /ActualText replacement

- A replacement placed by outlines or pictures, some of them excluded, fails filtered extraction as one with excluded glyphs does.
- Glyphs still place a replacement that has them.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: a long-named field that can't hold a signature leaves the walk whole

- A leaf of another type passed over for its name is no signature field and has none below it, so an unsigned form stays unsigned.
- A signature field, or any field with kids, still counts as unread.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: status captions use document faces only

- Each script takes the face PdfCraft writes it into documents with, so Chinese stays English although the pinned craft-fonts' interface-only Chinese face is meant for Hani.
- The craft-fonts usage line names Devanagari: the pinned revision has a face for it, and Hindi captions are drawn in it.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: refuse a new signature field the field walk can't read back

- Signing adds a field only when the walk reads the whole form with it: past a cap the signature would be listed as unread fields, and its name could repeat an unread field's.
- A field the walk already reads is signed as before.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: refuse a new signature field with a blank name

- The walk lists no field without a name, so signing now also requires the walk to list the new field: one named only with spaces or NULs would be a signature nothing shows.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: an unnamed signed field or an unreadable form counts as unread

- A signed field with no usable /T, or an /AcroForm or /Fields of the wrong type, cuts the field walk short: the document keeps its unread-fields row and stays signed.
- Signing refuses a blank new field name before it adds the field, so the refusal still says why.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: no display copy when no signature state is drawn

- display_bytes_for makes a copy only when it added an appearance or drew a state, as the app does.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: the verdict mark stays on the page

- A widget past the crop box takes its mark in the part on the page, or none when too little of it is there.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: typed values stay drawn while a signature redraws every page

- A form edit that changes a signature's state puts its values back in unbaked after the pages are invalidated.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: the action, Save As and shading tests check their effects

- The signed action and Save As tests reopen the output for the title and the comment; the shading test checks its clip.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: values typed earlier stay drawn while a signature redraws every page

- A form edit that changes a signature's state keeps every value still waiting on its page, not only its own.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: comment and content edits keep typed values drawn too

- An edit to one page's comments or content that changes a signature's state moves no page, so the values still waiting on their pages stay pending, as after a form edit.
- The pending-value test runs a field deletion and a comment after a fill, under a certification that allows the fill; its messages say what it checks.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: the language-change test skips without catalogs

- Like the other language tests, it returns early when the build has no craft-i18n catalogs.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: signed fields behind kids without a name count

- A kid with no /T but kids of its own is walked as a field node, so a signed field under it is listed by its full name.
- An unnamed kid claiming a signature its field doesn't have cuts the walk short, so the document stays signed and keeps its unread-fields row.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

---------

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
2026-11-07 20:37:17 -06:00
oracola91 f55e6163b6 Hidden layers reads each usage rule and layer list once
Many usage rules can name one long layer list. Each rule, each list and
each layer is now looked at once, with sets for the membership checks,
so the work grows with the file and not with how often it repeats a
list. Which layers count as hidden does not change.
2026-11-07 17:05:10 +02:00
oracola91 eee967330a Emptied layer sub-lists and direct layer references go too
An /Order sub-list or radio-button group whose layers were all removed
stayed behind, an /Order heading with it, and a list or usage rule that
named a removed layer directly instead of in an array kept that layer
in the file. Such sub-lists and groups are now dropped, a top-level
list keeps its place, and a direct reference is treated as an entry.
2026-11-07 17:00:46 +02:00
oracola91 68e2c65f55 Shared layer lists are cleaned once, not copied per path
Removing hidden layers copied each array a configuration list names by
reference, once for every path that reaches it, so lists that name one
array many times could grow without bound. Each array, usage rule and
configuration named by reference is now cleaned once where it is, and
the lists keep the reference.
2026-11-07 16:59:23 +02:00
oracola91 e27d8d9cc1 Unlinking undrawn images matches what a reader draws
A page's content streams are read as one, and forms named through a
reference-only object or listed in a Type 3 font's own resources are
read too, so the images they draw stay. A page tree that lists a node
twice is left alone. The pass repeats while names go, so an image only
an unused form drew now leaves with that form.
2026-11-07 16:58:46 +02:00
oracola91 98807de6b2 Remove Hidden Information keeps an unlink that was its only removal
Unlinking images nothing draws ran before the check for an empty result,
so a run where it was the only removal returned NothingToApply. The
remove-hidden edit and the Action Wizard step then dropped the working
copy, and the image stayed in the file. A run that unlinked anything now
succeeds and asks for the full rewrite.
2026-11-07 16:58:29 +02:00
oracola91 f7d8fbf21d Type 3 pruning decodes a page's appearances within one byte allowance
prune_type3_from_resources decoded every appearance without resources of
its own into one list before counting any operators, each up to the
single-stream limit, so their sum had no bound. They are now decoded one
at a time against one page's DecodeBudget, as the page content is, and
passing it refuses the redaction.
2026-11-07 16:53:20 +02:00
oracola91 a842564e3f Merge branch 'fix/type3-cleanup-keeps-other-drawers' into fix/type3-appearance-ops-total 2026-11-07 16:53:20 +02:00
oracola91 f0e400c1ef Type 3 pruning leaves tiling patterns to the pattern guard
A tiling pattern must have /Resources of its own, so its cell never
draws through the page's. Since #3680, Apply Redactions refuses a page
whose tiling pattern shows text or has no /Resources (pattern_guard), so
prune_type3_from_resources reads only soft mask groups now. Remove Hidden
Information runs no pattern guard: there, a page font that only such a
malformed pattern uses goes, as it did before this PR.

unlink_type3 still follows a pattern's cell, for patterns on pages the
redaction does not touch. Its test now puts the pattern on a second page.
2026-11-07 16:53:19 +02:00
oracola91 c3b1a7fd5f Merge remote-tracking branch 'upstream/main' into fix/type3-cleanup-keeps-other-drawers 2026-11-07 16:19:58 +02:00
Grzegorz MartensandClaude Opus 5.5 0916bec6ef M2.6: a font's streams read /DecodeParms given by reference
The redaction proof now reads a font's /ToUnicode with its /DecodeParms given by reference, and
`Metrics::from_dict`, which redaction takes the removed text from, read it only with direct ones.
Behind a predictor so described, the two got different text for the same codes, so a copy the
redaction rightly left counted as a survivor and the proof failed a sound redaction
(`a_unicode_map_behind_parameters_given_by_reference_is_read_alike`).

Every stream `from_dict` reads now goes through `Document::with_direct_parms`: the /ToUnicode; a
composite font's /Encoding, which sets how its codes split
(`cmap_streams_read_their_parameters_given_by_reference`); a subset's program and /CIDToGIDMap,
which say which of its CIDs have a glyph
(`a_subsets_program_and_cid_map_read_their_parameters_given_by_reference`); and the program a
simple font without /Widths is measured with
(`a_font_without_widths_measures_with_its_embedded_program`). Read without them, as on `main`,
such a font was measured with widths guessed from its name, so redaction removed glyphs the mark
doesn't cover and left some it does, and the proof, which also guesses from the name, passed.
Redaction now measures such a font with its program. The proof still guesses, so it fails that
redaction, as it already does on `main` when the program's parameters are direct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-07 13:49:52 +01:00
Grzegorz MartensandClaude Opus 5.5 1d0c303466 M2.6: the redaction proof reads /DecodeParms given by reference
The proof that came with #5557 decodes every stream it sweeps through `Ctx::decode`, which called
`decode_strict` with the stream alone. This PR's `decode_strict` also takes the document, to read
`/DecodeParms` given by reference, so the branch merged with main no longer built. `Ctx::decode`
now takes the document, and each of its callers passes the one it reads, so the proof reads such
a stream as redaction reads page content and forms. Before, it decoded the stream with the
parameters' defaults: a PNG predictor's rows kept their tag bytes, and a copy of the removed text
split by them went unreported (`a_survivor_behind_parameters_given_by_reference_is_found`). The
test helpers' streams are direct, so they pass an empty document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-07 13:15:45 +01:00
Josias Eli Rodriguez 65ebdab392 redact, annot: usable quad values on the f32 grid; appearances resolve indirect quads
Review round on #6525 (CodeRabbit, plus eliets2's first-pass finding):

- usable_quads returns usable values on the f32 grid, so readers folding
  f32 and f64 agree on the numbers as well as on the usable set
  (67108869 is 67108872 for every reader). New geom test pins it.
- The end-to-end redact agreement test gains the 4e38 case.
- Appearance drawing read /QuadPoints off the raw dictionary, dropping an
  indirect array or indirect entries the other readers keep.
  appearance::build now takes the document and resolves both, as redact's
  reader does; the restyle probes pass the document they already hold.
  New test pins both indirect shapes against the direct output.
- One shipped test block reformatted (rustfmt).

Drafted with AI assistance; verified by the contributor, fail-before
included.
2026-11-07 05:58:15 -06:00
Grzegorz MartensandClaude Opus 5.5 749a323342 M2.6: tests pin damage in an earlier filter stage, on another page, and in the saved file
- cos: a filter chain whose first stage stops at damage and whose
  second reads all it was given is damaged.
- optimize: a page damaged partway stops resampling for the whole
  document, so another page drawing the same image whole doesn't get
  it resampled.
- redact: after redacting content with /DecodeParms by reference, no
  stream in the saved file holds the text, read with its parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-07 12:37:35 +01:00
Grzegorz MartensandClaude Opus 5.5 2d672d9517 M2.6: redaction keeps an image the page still draws behind parameters by reference
Once a redaction removes an image or form under a mark, it takes the name
out of the page's resources unless the page's content still draws it
elsewhere. It read the page's streams again for that with `decoded`, which
reads `/DecodeParms` only when direct, so a stream behind a predictor whose
parameters are an indirect object seemed not to draw it, and the page lost
the image there.

It now looks in what it read for the redaction, with the parameters by
reference, and decodes again only the streams it has just written, which
have none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-07 12:37:35 +01:00
Grzegorz MartensandClaude Opus 5.5 91968610c3 M2.6: redaction reads content with its /DecodeParms given by reference
`DecodeBudget::decode_strict` read a stream's `/DecodeParms` only when
direct, so content behind a predictor whose parameters are an indirect
object decoded with the predictor's tag bytes left in. Redaction then found
no glyph under a mark, left the text in the file and reported the mark
applied. ISO 32000-2 lets those parameters be indirect.

`decode_strict` takes the document and reads through `decoding_view`, as
`decode_with_damage` does. Its other readers read the parameters too:
Remove Hidden Information's hidden text and layers, the audit's covered-text
check, and edit's backdrop scan.

The accessibility_mark_tag_artifact description says that content which is,
or may be, in a stream damaged partway is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-07 12:37:35 +01:00
vangel76andClaude Sonnet 5.5 81af7f1cd9 Merge upstream/main into feat/redact-mark-regex
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 11:54:30 +01:00