Commit Graph
3325 Commits
Author SHA1 Message Date
mtlcl410andClaude Opus 5.5 dcef0aebff M8: bound the removed text kept for the metadata check per document (#2448)
The run and byte limits were per page, and a glyph's /ToUnicode text can
be 256 UTF-16 units, so a short content stream could keep gigabytes of
removed text across pages. The document now keeps at most 16 MiB; past
that the rest is not kept and the metadata check fails closed, as for any
other text it couldn't keep (CodeRabbit's retained concern on #2539).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyG234xeYkJfr41E6iP
2026-11-02 23:36:21 +08:00
mtlcl410andClaude Opus 5.5 6ce5b5fe43 M8: a removed document XMP packet's PDF/A and PDF/UA identification is written back, or reported lost (#2448)
#2539 review: the scrub removes the catalog's XMP packet whole when it holds
the removed text, and with it the file's PDF/A and PDF/UA claims, which
Report didn't say. The identification (pdfaid part, conformance, amd, rev;
pdfuaid part, rev) is now read by namespace (roxmltree, already in the build
through pdfcraft-xfa), each value kept only when it is the short code it must
be and doesn't hold the removed text (one-character codes always), and
written back in a packet holding nothing else, with a fixed pdfuaid extension
schema under PDF/A-1 to -3 (left out, with the PDF/UA claim, if the redacted
words are among its words). Report::identification says, per standard,
None / Kept / Lost; the engine passes it on as
EditNotice::RedactionIdentification, the Apply notification says it after the
count, and redact_apply returns identification { pdfa, pdfua }.

Tests (redact): the_identification_of_a_removed_packet_is_written_back,
a_packet_without_identification_is_only_removed,
an_identification_that_cannot_be_kept_is_reported_lost,
unsearched_text_keeps_only_one_character_codes,
the_extension_schema_is_left_out_when_it_uses_a_redacted_word; engine
applied_redactions_report_the_pdfa_identification_written_back; automation
redact_apply_reports_the_identification_written_back; ui-egui
applying_says_the_pdfa_identification_was_written_back. With the write-back
replaced by "nothing kept", the redact tests fail (Kept expected, None
found); with the notice not pushed, the engine, automation and UI tests fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-02 01:16:34 +08:00
mtlcl410andClaude Opus 5.5 17a9345064 M8: an FF FE text string is searched as UTF-16LE, in /Info and the places applying leaves (#2448)
#2539 review: ISO 32000-2 §7.9.2.2.1 defines text strings in PDFDocEncoding,
UTF-16BE (FE FF) and UTF-8 (EF BB BF) only, so an FF FE string was read
through PDFDocEncoding and Cyrillic or CJK text in it could never match.
It is now read as UTF-16LE for the search (fail-closed), in the /Info scrub
and in the bookmark, attachment and comment counts.

Test: a_utf16le_string_is_searched_as_utf16le (failed before: 0 /Info
entries removed; with only the count reverted, bookmarks 0 instead of 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-02 00:56:20 +08:00
mtlcl410andClaude Opus 5.5 38030a7d54 M8: the metadata check compares NFKC, lower-case text, so Unicode forms can't hide the removed text (#2448)
#2539 review: a decomposed "Müller" in a UTF-16BE /Title, a U+FB01 ligature
from a ToUnicode map and full-width letters never matched their plain forms.
Both sides are now normalized to NFKC and lower case (unicode-normalization,
already in the build through pdfcraft-crypt's stringprep, so no new crate).
Normalized text more than four times its input's length (U+FDFA and the like)
isn't searched: it counts as holding the text.

Tests: unicode_forms_of_the_removed_text_match (failed before: Title, Subject
and Keywords kept), a_value_that_normalizes_to_far_more_text_counts_as_holding_it
(failed with the growth limit raised to 100: 0 removed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-02 00:54:23 +08:00
mtlcl410andClaude Opus 5.5 fdcc569094 M8: the metadata check also compares letters and digits alone, so spacing and TJ tracking can't hide the removed text (#2448)
#2539 review: a heading tracked with TJ numbers ([(B) -300 (L) -300 ...] TJ)
was collected as "B L U E F I N", which neither held "Bluefin" nor had a word
of three letters, and a value spelled "Blue fin" or "BLUE-FIN" didn't hold the
term "bluefin". A TJ gap can't be told from a word space, so the glyph ends stay
as they are; instead every term with three letters or digits is also looked
for by its letters and digits alone, and a value is part of a run when its
letters and digits are, starting and ending at the run's word edges ("True"
still isn't part of "untrue"). It over-matches only towards removal.

Tests: a_heading_tracked_with_tj_numbers_matches_the_metadata,
a_value_spaced_or_hyphenated_otherwise_matches (both failed before: /Title
kept, metadata 0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-02 00:51:48 +08:00
mtlcl410andClaude Opus 5.5 3e025b9143 M8: the places applied redactions leave holding the removed text are an EditNotice (#2448)
Main's `EditNotice` is "something the last edit did that the user should hear
about", set by `Session::apply` and cleared by the next edit, undo and redo,
which is what the PR's separate `redaction_elsewhere` field did by hand. It is
now `EditNotice::RedactionElsewhere(Elsewhere)`, pushed when a place is counted
or the text couldn't be searched for: tools list it in `warnings` with the
other notices, the interface says it through the same path, and
`Document::redaction_elsewhere()` gives `redact_apply` and the Apply dialog the
counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-02 00:44:54 +08:00
mtlcl410andClaude Opus 5.5 069f55eb12 Merge storytold/pdfcraft main into fix/redact-metadata-2448
Conflict in crates/engine/src/document.rs: main turned `edit_notices` into
`Vec<EditNotice>`; the PR's `redaction_elsewhere` field is kept beside it in
this merge, and the next commit makes it an `EditNotice` variant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-01 23:58:34 +08:00
mtlcl410andClaude Opus 5.5 4dbc4fac69 M8: the apply notification says where the removed text is still held (#2448)
After "Applied N redaction marks…", the same notification names the bookmark titles,
attachments, comments and other metadata streams that still hold the redacted text, and points
to Remove hidden information. New English templates; untranslated text shows in English. The
protect.apply-redactions parity note and evidence say what is reported and what is still left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-01 18:31:52 +08:00
mtlcl410andClaude Opus 5.5 d37bde8c3b M8: Apply redactions passes on where the removed text is still held; redact_apply returns it (#2448)
The document keeps the last apply's Report::still_holding as redaction_elsewhere (re-exported as
RedactionElsewhere), cleared by the next edit, undo or redo as edit_notices is. redact_apply
returns it as still_holding with the four counts, their total and unsearched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-01 18:31:52 +08:00
mtlcl410andClaude Opus 5.5 1fd2ff31bb M8: test that a form whose content can't be decoded clears the metadata (#2448)
The unread-form branch was tested for a form nested past the depth limit only; a form under a
mark whose filter can't be decoded is removed unread too, and now has an end-to-end test, seen
failing with that branch's flag removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-01 18:09:13 +08:00
mtlcl410andClaude Opus 5.5 81d629d1ff M8: applying redactions counts the bookmarks, attachments, comments and XMP that still hold the removed text (#2448)
Report::still_holding (Elsewhere) counts the places apply leaves as they are that still hold the
removed text: bookmark titles, attachment names, file names and descriptions, the text and authors
of the comments that remain, and XMP on objects other than the catalog and the pages. Nothing is
changed; Remove hidden information removes them. The text is matched as the metadata check
matches it (one Search type now serves both), with its own work limit. When the removed text
couldn't be searched for, every place holding any text counts and `unsearched` says so. The walks
read each outline item and name-tree node once, within caps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-01 17:51:37 +08:00
mtlcl410andClaude Opus 5.5 ca4ddcfb90 M8: a test for each way the metadata check fails closed; XMP is decoded strictly (#2448)
End-to-end tests for a removed run over 64 KiB, more than 20,000 terms, a run over 4,096
characters, and XMP that is corrupt, cut short, behind two filters, over 16 MiB or in another
encoding. Each was seen failing with its guard removed or loosened.

The corrupt and cut-short cases showed that the tolerant decoder returned the part of the packet
before the damage, which was then searched as if it were the whole packet. XMP is now decoded
strictly, and a packet behind more than one filter goes unread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-11-01 17:46:19 +08:00
FredApps f2e1a8ce29 Merge branch 'main' into fix/1577-optional-page-edge-shadows-integration 2026-10-31 23:38:40 +01:00
FredApps a471cb1664 Merge pull request #1433 from srt8hellcat2-ui/feature/two-paged-cli-1354
M3: add optional --two-paged CLI flag (fixes #1354)
2026-10-31 23:35:31 +01:00
Combiler01 b77699a22b test(optimize): accept Flate or CCITT compression in monochrome scan test 2026-10-31 22:50:50 +01:00
Combiler01 0475725247 test(optimize): accept Flate or CCITT compression in monochrome scan test 2026-10-31 22:50:50 +01:00
Combiler01 9a655ddb2d test(optimize): accept Flate or CCITT compression in monochrome scan test 2026-10-31 22:50:50 +01:00
FredApps 1d5ce9d7a9 Update shadow regression wheel events and retain upstream parity notes 2026-10-31 22:26:16 +01:00
FredApps bad4c81800 Merge current main into PR2088 and port shadow preference to split UI modules 2026-10-31 22:13:25 +01:00
FredApps 286a3c6eee Merge current main into PR1433 and preserve launch override across module splits 2026-10-31 22:12:05 +01:00
FredApps 7165ff1ace Merge current main into PR4279 and retain unsigned delete-save policy 2026-10-31 22:09:56 +01:00
Dylan M. Taylor f137a289ba Merge pull request #5522 from aiongg/docs-tidy
docs: tidy agent, contributor and asset docs
2026-10-31 01:06:25 -04:00
aiongg 635a840164 updates: show check results with craft-update's CheckResultDialog 2026-10-31 04:43:07 +00:00
aiongg 44ddb348a9 docs: tidy agent, contributor and asset docs 2026-10-31 04:17:20 +00:00
mtlcl410andClaude Opus 5.5 409477067a M8: the redact README lists every fail-closed trigger and the metadata it doesn't scrub (#2448)
Review on #2539 (nitpick): the README named only the collection limit
and unreadable XMP as fail-closed cases. It now lists all of them (the
run and run-length limits, a removed glyph with unknown text, a form
XObject removed unread past the depth limit or unreadable, the term
limits, the search work limit, oversized or undecodable XMP) and the
metadata the check leaves to Sanitize: XMP on objects other than the
catalog and pages, bookmark titles, and attachments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
2026-10-31 08:05:51 +08:00
songzhihui a57095c872 M2: assert exact GBK glyph rasters for installed Chinese families 2026-10-31 01:39:00 +08:00
songzhihui 9447cdc810 M2: merge main and resolve CJK viewer integration conflicts 2026-10-31 01:14:02 +08:00
FredApps 6772772440 test: accept preserved CCITT filter arrays in lossless regression 2026-10-30 15:52:18 +01:00
FredApps 7fe33d1cbd test: verify conservative review fixes across rendering and automation 2026-10-30 15:32:44 +01:00
FredApps 4791cacd2c test: qualify conservative tone retention and format review regressions 2026-10-30 14:55:44 +01:00
FredApps d7f3e67141 fix: bound backdrop edits and withhold unsafe integration behavior 2026-10-30 14:45:50 +01:00
FredApps 673178b70c fix(edit): require intact streams when identifying page backdrops 2026-10-30 13:21:04 +01:00
FredApps 25f3d12960 style: format checked soft-wrap budget assignment 2026-10-30 12:54:56 +01:00
FredApps ee71348bc8 fix: bound soft-wrap metadata and preserve unknown backdrop content 2026-10-30 12:47:37 +01:00
FredApps 6d7f739e3c test: enforce page press routing and record integration regression evidence 2026-10-30 12:38:09 +01:00
FredApps bdf56209ce style: format combined paragraph regression 2026-10-30 11:08:55 +01:00
FredApps 7d3d4830e5 Integrate remaining six PdfCraft parity PRs with conflict resolutions
Includes #4325 #1952 #4180 #2529 #4320 #4805, preserving their original histories.
Keep paragraph alignment and hard/soft wrap metadata together; retain CCITT/downsampling/RGB ownership guards with bounded JBIG2 export and exact-copy symbol encoding.
Combined interaction regressions added. Validation pending.
2026-10-30 11:02:07 +01:00
FredApps a07f94f2f7 test: supply Fill & Sign colour in combined window-title fixture 2026-10-30 10:46:11 +01:00
FredApps b21af3b969 Integrate 21 clean-merging PdfCraft parity PRs
Preserve each original PR head and its authorship as a merge parent.
Includes #4304 #4335 #4384 #4404 #4179 #5300 #4719 #4541 #2815 #4620 #3789 #3779 #2960 #5208 #4596 #3636 #4494 #4013 #5134 #2133 #2635.
Conflicting candidates skipped. Combined CI pending; see docs/integration-20261030.md.
2026-10-30 10:35:42 +01:00
FredApps 4b319bb383 Keep typed font sizes accessible and within the current panel layout
Use a named compact preset arrow beside the field, retain the row alignment/width assertions for the editable control, and open the current colour flyout in the saved-text regression. No accessibility or layout checks are disabled.
2026-10-30 09:29:42 +01:00
FredApps e19f964690 Handle the current fallible FDF export API in the round-trip regression 2026-10-30 09:10:25 +01:00
FredApps 7227eb68ab Name the Pages sidebar keyboard group and record its accessible snapshot 2026-10-30 09:10:05 +01:00
FredApps ee82cf1a72 Remove extra blank lines after relocating font-size controls 2026-10-30 08:58:13 +01:00
FredApps b07a012cd7 Reconcile button export values with current form authoring modules
Preserve caption editing and current form/Prepare module boundaries. Carry export-only appearance preservation, validation and data round-trip tests to their current paths, and retain current attribution metadata. Hosted CI must validate this refreshed head.
2026-10-30 08:56:21 +01:00
FredApps ea66a642fe Reconcile Pages sidebar selection with current keyboard and panel modules
Preserve main's continuous text selection, page tools and module split. Carry only this branch's sidebar-focus feature and regression onto main, without resurrecting old common files from criss-cross merge bases. Hosted CI remains required.
2026-10-30 08:52:06 +01:00
FredApps 6b38a5ab47 Reconcile typed font sizes and Fill & Sign colours with current UI
Use the unified Format Text panel and current page-input context instead of restoring old monolithic UI code. Preserve main's pointer routing, editor accessibility and preferences; retain colour, save and input regressions. Hosted CI must validate this refreshed head.
2026-10-30 08:47:42 +01:00
mtlcl410andClaude Opus 5.5 aed4599ed1 M8: the unread-form metadata test nests past main's form depth limit (#2448)
main raised the redaction pass's form depth limit from 12 to 50 (#3645),
so the 16-deep chain was walked and the test's form was no longer removed
unread. The chain is now built from MAX_DEPTH, one level past it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-30 13:58:50 +08:00
mtlcl410andClaude Opus 5.5 5b2048ef25 M5: a removed glyph whose text isn't known makes the metadata check fail closed (#2448)
Review on #2539: a removed glyph with no known text (no /ToUnicode, a
glyph name outside the list) went in as an empty string and nothing marked
the collection as incomplete, so when every removed glyph was like that
the terms were empty and /Info and XMP were left untouched. It now counts
as text the check couldn't keep, and every /Info string and XMP stream goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:58:50 +08:00
mtlcl410andClaude Opus 5.5 d64a363f46 M8: metadata search decodes character references with any number of leading zeros (#2448)
Review follow-up: `&#00000000000066;LUEFIN` hid BLUEFIN from the 13-byte scan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:58:50 +08:00
mtlcl410andClaude Opus 5.5 56ce7661d6 M8: one search budget per metadata scrub, bounded entity scan, fail closed for unread forms (#2448)
Review by Losminek on #2539:
- The work cap was checked per value, so very many small /Info or XMP
  values could add up without bound. One budget now covers the whole
  scrub; past it every further entry or packet counts as matching
  (fail-closed). Identical removed runs are searched once.
- unescape looked for ';' through the rest of the text before checking
  the 12-byte entity limit, so a packet of bare '&'s was quadratic. It
  now looks only at the next 13 characters (200,000 '&': 943 ms -> 5 ms).
- A form XObject removed whole past the depth limit, or when its stream
  doesn't decode, never had its text read; both paths now set
  removed_cut so the metadata check clears everything instead of
  searching incomplete text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:58:49 +08:00