Commit Graph
2424 Commits
Author SHA1 Message Date
Eric Kaden dd6d7feadb M12: a date is tried in the field's first pattern again only when its format refused it 2026-10-22 22:18:11 -04:00
Eric Kaden 91df3c01a8 Merge remote-tracking branch 'upstream/main' into fix/xfa-edit-picture-alternatives
# Conflicts:
#	crates/xfa/src/tests.rs
2026-10-22 21:54:56 -04:00
Dylan M. Taylor d31a96f46f Merge pull request #4121 from aiongg/fix/coderabbit-c1-js-xfa
fix(js, xfa): bound the text and work that form scripts and XFA data can multiply
2026-10-22 21:39:08 -04:00
Dylan M. Taylor 8ee1ac0c58 Merge pull request #4117 from aiongg/fix/coderabbit-b-caps
fix(limits): bound the decoding and listing work that repeated references and small inputs multiply
2026-10-22 21:38:59 -04:00
Dylan M. Taylor 3cee9f338f Merge pull request #4128 from aiongg/fix/coderabbit-c4-core
fix(cos, content, filters, crypt, model, render, print, a11y): bound the work that repeated keys, streams and page values multiply
2026-10-22 21:38:37 -04:00
Dylan M. Taylor f7a3a52fad Merge pull request #4114 from aiongg/fix/coderabbit-b-shared
fix(cos): one decode budget per page walk, so repeated stream references cannot multiply decoding
2026-10-22 21:38:15 -04:00
Brandon ThomasandClaude Opus 5.5 53199fb4fa Release 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.13.0
2026-10-22 20:06:13 -04:00
aiongg 03366c3c70 A counted decode bounds all filter stages together and counts output before the predictor 2026-10-22 23:33:37 +00:00
aiongg 4362449fb2 fix(render): find an embedded file by key without the listing's budget
Extraction walked the name tree again with a fresh key budget, so keys
the listing had left out could fill it and a listed embedded file came
back as not found. The lookup compares each key with the one wanted and
keeps none, so it needs no budget; a key object or /Names array that
several places reference is converted once.
2026-10-22 23:32:42 +00:00
aiongg 8bcf83faef fix(render): spend the raw length of text that converts shorter
A kept web address or attachment text spent only its converted length,
so a long raw value that trimmed short was converted in full for every
entry sharing it. A kept conversion now spends the longer of its raw
and converted lengths.
2026-10-22 23:31:43 +00:00
aiongg 7a73c2ec3a fix(render): keep embedded-file keys within the text budget
The embedded-files name tree copied every key, and an attachment
without a file name copied its key again as the name, so many entries
sharing one long key string multiplied it. Keys, and a key kept again
as the name, now spend the 16 MiB link and attachment text budget; an
embedded file whose key does not fit is not listed.
2026-10-22 22:03:43 +00:00
aiongg a0a66b982b fix(render): read an annotation attachment's Contents only when it names it
An annotation attachment's /Contents was read and charged to the text
budget before the file specification's /UF or /F, which then named the
attachment, so the discarded text could leave a later web address
unlisted. /Contents is now read only when neither name is there.
2026-10-22 22:02:10 +00:00
aiongg 0c09000f7b A decode charges the page's decode budget for every filter stage's output 2026-10-22 22:00:51 +00:00
aiongg aaa4b99eb9 fix(render): spend the text budget on a rejected conversion
A web address or attachment text that converted to more than the budget
left spent nothing, so every link or attachment sharing it converted it
again. A conversion that comes out too long now spends the raw length,
and so does attachment text that trims to nothing.
2026-10-22 21:54:39 +00:00
skylar taylor-barrick adea2c908a M14: recover existing macOS PDF associations after open and save (#3558)
* M14: recover existing macOS PDF associations

* M14: address macOS association recovery review

* M14: reuse upstream save metadata preservation
2026-10-22 15:49:13 -06:00
aiongg d67c4b3059 fix(js): an XFA value past the effect cap leaves the field unchanged 2026-10-22 21:41:50 +00:00
aiongg e541151389 fix(js): XFA presence and access change the snapshot only when their effect is kept 2026-10-22 21:41:01 +00:00
aiongg 11d2a576d6 Filters README lists decode_counted 2026-10-22 21:40:10 +00:00
aiongg 0ae289c762 A failed strict decode charges the page's decode budget for what it decoded 2026-10-22 21:37:19 +00:00
aiongg fd2e13f332 M8: XFA instance changes reserve their effect before changing the snapshot 2026-10-22 21:35:52 +00:00
aiongg 426a609795 fix(render): keep link addresses and attachment text within 16 MiB
The link and attachment listings copied a link's web address and an
attachment's name and description for every annotation or name-tree
entry, so many entries sharing one long string multiplied it in
memory. Together they keep at most 16 MiB of that text, and a string
past what is left is not converted. A link past it lists as a URI
action without its address, an attachment under its generic name
without a description.
2026-10-22 21:35:23 +00:00
aiongg 4e1457ed4f Marking a tag as an artifact refuses content past the page's decode budget 2026-10-22 21:33:43 +00:00
aiongg c82dc91abf fix(render): list an annotation reached through a reference chain once
The annotation listing skipped a repeated /Annots entry by its raw
reference, so an indirect object holding only a reference to a link
listed that link a second time. Entries and page arrays are now told
apart by the object their reference chain ends at. The lopdf reader
reported the first object of a chain, because lopdf's get_object
already follows it; it now reports the last, as the lazy reader does.
2026-10-22 21:33:20 +00:00
aiongg 473b0a4371 Merge fix/coderabbit-b-shared into fix/coderabbit-c4-core 2026-10-22 21:32:09 +00:00
aiongg 4fc56fd303 Reading blocks documents that over-budget page content is an error and truncation covers forms 2026-10-22 21:29:04 +00:00
aiongg f63f509647 fix(cos): compare each section's table with a repeated hybrid stream
A section naming an /XRefStm that a newer section had already read
skipped the stream entirely, so an entry in its own table that
disagreed with the stream was no longer recorded in xref_oddities.
Signature validation reads those records to refuse an appended section
another reader could follow. The reader keeps each hybrid stream's
entries (up to MAX_XREF_OBJECTS in all) and compares every later
naming section's table with them, without decoding the stream again.
2026-10-22 20:37:04 +00:00
aiongg 0e643d45a0 Merge #4114 (fix/coderabbit-b-shared) into fix/coderabbit-c4-core
#4114 moves the bodies of Stream::decoded_within and decoded_strict into
decoded_within_typed and decoded_strict_within_typed, which return
FilterError so DecodeBudget can tell a spent limit from corrupt data.
This branch checks /Filter with readable_filters there. Kept #4114's
typed functions and made readable_filters return
FilterError::Unsupported, so a /Filter that is not a name fails both
typed decodes without charging the budget; decoded_first_filter_strict
maps it to CosError as before.
2026-10-22 20:30:25 +00:00
aiongg 60132bbfbf Merge upstream/main into fix/coderabbit-c4-core 2026-10-22 20:29:34 +00:00
aiongg 1e2ac1d85a fix(xfa): copy embedded field values out only as far as the text budget
The text layout cut each embedded value to the bytes its block had left, but
the resolver cloned the whole value first. With a byte or two left and a
value starting with a multibyte character, every later run cloned the full
value and kept none of it. The resolver is given the bytes left and returns
only a prefix that fits.
2026-10-22 20:21:21 +00:00
aiongg 2ece498ffc fix(js): charge both copies of a written XFA value before keeping either
set_value charged the snapshot's copy, changed the field, then charged the
effect's copy in push. When only the first fit, the effect was dropped while
later reads in the script saw the new value. Both copies are charged at once
before the field changes.
2026-10-22 20:21:21 +00:00
aiongg 2019726eae Merge upstream/main into fix/coderabbit-c1-js-xfa 2026-10-22 20:16:55 +00:00
aiongg 668d8f777e fix(xfa): every drawn subform instance spends the layout budget
A positioned root draws its children instead of flowing them, so nested
repeating subforms under it multiplied their instances without spending
the measurement budget that flowed instances spend. Drawing a subform
instance spends it too.
2026-10-22 20:08:33 +00:00
aiongg a887405ea1 fix(sign): don't report valid when a DSS cap hides revocation evidence
The evidence caps counted repeated references to one stream, so a /DSS
array could list one benign stream 1024 times ahead of a revoking CRL,
and the check never reached it. Repeats are skipped before the entry
cap counts anything, and when a cap still stops an array being read in
full without a revocation found, the signature's status is unknown
rather than valid, with the reason in its details.
2026-10-22 20:07:41 +00:00
aiongg 071130d498 fix(render): list links and attachments after the comment budget fills
Reaching the 64 MiB comment budget ended the whole annotation scan, so
links and file attachments listed after it were missing, and a later
JavaScript link left the document unmarked. Past the budget, comments
are no longer built, and the scan goes on for links and attachments.
2026-10-22 20:05:05 +00:00
aiongg 02f0f7396f fix(annot): count web-address text after its UTF-8 conversion
An address of invalid UTF-8 grows up to three times when converted, so
the listing's 16 MiB cap counted fewer bytes than it kept. The cap now
counts the converted text; the raw length is still checked first, so an
address already over the cap is never converted.
2026-10-22 20:04:04 +00:00
aiongg d818e4a20a Merge upstream/main into fix/coderabbit-b-caps 2026-10-22 20:03:27 +00:00
aiongg f02b998122 Format the accessibility tests 2026-10-22 19:54:10 +00:00
aiongg 9d5bcc0745 Accessibility check asks for a manual check where page content ran past the decode budget 2026-10-22 19:51:47 +00:00
aiongg 089882f372 Marking a figure decorative refuses content past the page's decode budget 2026-10-22 19:50:42 +00:00
aiongg f980b34ef3 Text and image reading charge a page's content and its forms to one decode budget 2026-10-22 19:49:44 +00:00
aiongg e2a6771641 Redaction charges a page's content streams and its forms to one decode budget 2026-10-22 19:48:33 +00:00
aiongg e0e7d4462e Cover audit charges each form entry to the page's form allowance 2026-10-22 19:46:50 +00:00
aiongg 3dbdc13b1d Image resolution walk charges each form entry to the page's form allowance 2026-10-22 19:46:11 +00:00
aiongg a72845ad80 Merge upstream/main into fix/coderabbit-b-shared
Redaction reads page content and form XObjects strictly (upstream) and
within one page's decode budget (this branch): both now go through
DecodeBudget::decode_strict.
2026-10-22 19:44:33 +00:00
Brandon ThomasandClaude Opus 5.5 39844f06b5 Release 0.12.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.12.0
2026-10-22 15:39:48 -04:00
aiongg 8555d6f159 fix(parity): cite the renamed renderer-check test and drop fixed redaction gaps 2026-10-22 15:17:44 -04:00
aiongg 01aa6bcc17 docs(redact): describe the overlay, text state, description and inline image rules 2026-10-22 15:17:44 -04:00
aiongg b72078871a fix(content): end inline images where the renderer ends them
Filtered inline image data has no length, so its end is an EI chosen
among candidates, and the content parser chose by a different rule
than the renderer's tokenizer (hayro-syntax): the renderer also takes an
EI with no whitespace before it, and checks a candidate by reading what
follows as operators. With image data such as "7FEI 1 0 0 1 200 0 cm",
the renderer ran a cm that redaction read as image data, so text was
drawn 200 pt from where removal and its verify pass looked.

The parser now takes each inline image's end from the renderer's
tokenizer (already in the tree through pdfcraft-cos), so both read the
same operators after the image; a test runs both on the same fixtures.
Only where that tokenizer reads no image (no EI it accepts, or
parameters it can't read) does the parser fall back to its own rules,
counting the image in Parsed::ambiguous_images, and applying redactions
refuses such a page as unreadable.
2026-10-22 15:17:44 -04:00
aiongg 7db37911ed fix(redact): clear descriptions above removed content and annotations
Structure cleanup cleared alternate text, actual text and expansions
only on the element that directly owned changed marked content, and an
element that lost an annotation reference kept its descriptions unless
it lost every kid. An ancestor's /ActualText covering a redacted span,
or a link element's /Alt repeating the address of a removed link, stayed
in the saved file. Elements that lose an annotation now always lose
their descriptions, and every ancestor of an affected element (found
through both /P and the tree's /K, which may disagree) loses its own.
An element written in place above one with a description to clear
refuses the operation, as other in-place cases already do.
2026-10-22 15:17:44 -04:00
aiongg 2cdb39503e fix(redact): compare marked content without a copy per enclosing MCID
Finding which marked content a redaction changed copied every operator
into the buffer of each MCID enclosing it, so content nested n deep was
held n times: a modest stream of nested BDCs could exhaust memory.
Each operator inside marked content is now serialized once, and each
MCID keeps the ranges its sequences span. The bytes compared are capped;
MCIDs past the cap count as changed, so their descriptions are cleared
rather than kept.
2026-10-22 15:17:44 -04:00