Commit Graph
249 Commits
Author SHA1 Message Date
eliets2andeliets2 a1ea2d3763 Align stream decode cap with renderer at 256 MiB (#6585)
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>
2026-11-09 18:54:53 +01:00
Harrison Booth b44257b92b Test preservation of PDF 2.0 structure metadata (#5073) 2026-11-09 18:27:50 +08:00
mtlcl410 9e29af7849 fix(render): a stream ending in endstrea is not read into a later object's stream (hayro-syntax patch 36) (#6669) 2026-11-09 18:03:41 +08:00
bflatastic 8dfb3bdc5b Merge origin/main into merge/preflight-pdfa-checks
# Conflicts:
#	crates/engine/src/tests.rs
2026-11-09 09:01:00 +07:00
bflatastic 2992146abb Merge #6312: M1: a document that encrypts only its embedded files opens without a password (core.attachment-only-encryption)
# Conflicts:
#	crates/render/src/inspect/tests.rs
#	vendor/README.md
2026-11-09 08:12:28 +07:00
bflatastic e70cacf36f Merge #6290: fix(render): a stream with no endstream after it draws from its /Length instead of a blank page (hayro-syntax patch 36)
# Conflicts:
#	crates/render/src/raster/tests.rs
#	vendor/README.md
2026-11-09 08:11:43 +07:00
bflatastic 9e2c8130ad Merge #6561: M1: a file whose last update has no /Prev opens with all its pages and a repair note (core.repair-broken-files)
# Conflicts:
#	parity/acrobat-features.toml
#	vendor/README.md
#	vendor/hayro-syntax/src/pdf.rs
2026-11-09 08:11:01 +07:00
bflatastic 6be3850177 Merge #6214: M11: PDF/A verify checks the file identifier and Save as PDF/A adds one (optimize.pdfa-validate)
# Conflicts:
#	crates/automation/src/tools.rs
#	crates/cos/src/lib.rs
#	crates/preflight/src/lib.rs
#	crates/preflight/src/tests.rs
#	parity/acrobat-features.toml
2026-11-09 08:10:01 +07:00
bflatastic e7ecc84544 Merge #6506: fix(render): a fax image with more /Rows than its /Height is decoded to its height (hayro-syntax patch 41)
# Conflicts:
#	vendor/README.md
2026-11-09 08:05:40 +07:00
bflatastic 5c42721193 Merge #6501: fix(render): an image whose PNG predictor was declared but never applied draws its samples (hayro-syntax patch 40)
# Conflicts:
#	parity/acrobat-features.toml
#	vendor/README.md
2026-11-09 08:05:23 +07:00
quincy-jc d0b2a32744 perf(optimize): work out pictures on threads and skip needless page reads (#6230) 2026-11-09 08:53:27 +08:00
Mex a50bc63292 M1: correct the COS object reference API example (#6183) 2026-11-09 08:45:25 +08:00
mtlcl410 0a029f0aad M12: floating fields show the values scripts give them, and the sheet number and count (#6160) (#6166) 2026-11-09 08:45:03 +08:00
jonathanwxh-cell 1f48670fce refactor(cos): consolidate duplicate civil_from_days onto pdfcraft_cos::civil_from_days (#6540)
Fixes #6466
2026-11-09 08:32:08 +08:00
mtlcl410andClaude Opus 5.5 b702338cc2 M1: the save test checks every reopened page's width in order (core.repair-broken-files)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 00:28:36 +08:00
mtlcl410andClaude Opus 5.5 64dd381bc4 M1: the unlinked-update tests check the objects the update wins with (core.malformed-input-safety)
The hybrid update test now checks that the update's page and catalog
win and that the repair log says why, and that the linked file keeps
both revisions; the save test checks that the reopened file keeps the
update's page and catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 00:04:59 +08:00
mtlcl410 fae489acdb Merge upstream/main into fix/update-without-prev-is-refused 2026-11-08 23:27:12 +08:00
mtlcl410andClaude Opus 5.5 90b3c41bc6 Merge upstream/main into fix/flate-predictor-untagged-rows
Conflict: crates/edit/src/rules.rs, where main joins the content streams with
crate::text::joined; that line replaces this branch's earlier build fix, so the file is main's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 23:17:19 +08:00
mtlcl410 7d1dea0517 M7: Ungroup draws Form artwork in place, so its text can be edited (#3067)
Refs #2964, #3005
2026-11-08 22:14:30 +08:00
mtlcl410andClaude Opus 5.5 d6ffacbd19 Merge upstream/main into fix/update-without-prev-is-refused
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 20:50:01 +08:00
mtlcl410 ca146cd23f Merge remote-tracking branch 'upstream/main' into lane1-6501 2026-11-08 20:31:22 +08:00
FredApps 6fa72b914c Merge pull request #4876 from vangel76/perf/write-full-retain-parsed-objects
perf(cos): write_full keeps the objects it parsed while numbering
2026-11-08 13:22:45 +01:00
FredApps efe75d3a30 Merge pull request #4874 from vangel76/perf/cos-reconstruct-4868
perf(cos): speed up byte searches during cross-reference reconstruction
2026-11-08 13:22:36 +01:00
mtlcl410 67ac32a2a1 Merge remote-tracking branch 'upstream/main' into lane1-6501
# Conflicts:
#	crates/filters/src/lib.rs
2026-11-08 20:21:16 +08:00
FredApps ed71db7c0f Merge pull request #4878 from vangel76/perf/cos-serialize-fmt-4870
perf(cos): write hex strings, integers and references without the formatter
2026-11-08 13:19:42 +01:00
FredApps 0a7798707b Merge pull request #5825 from Piohrun/fix/flate-input-work
fix(filters): inflate with zlib-rs everywhere, and charge the image allowance for what Flate reads
2026-11-08 13:11:25 +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
mtlcl410andClaude Opus 5.5 d6750ee660 M11: ensure_id's doc says it keeps any two-element /ID array, even a malformed one (optimize.pdfa-validate)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 11:58:19 +08:00
vangel76andClaude Sonnet 5.5 f19ecc4635 Merge upstream/main; drop the object-stream pre-check from reconstruct
Main now reports unreadable objects from the parse of every object in
Document::reconstruct, so the pre-check that skipped that parse is gone.
The single-pass /XRef positions and the rarest-byte find stay.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 18:49:31 +01:00
vangel76 8fba397412 Merge remote-tracking branch 'upstream/main' into perf/cos-reconstruct-4868-w 2026-11-07 18:48:15 +01:00
mtlcl410andClaude Opus 5.5 4e522cd067 Merge upstream/main into fix/update-without-prev-is-refused
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 00:25:48 +08:00
vangel76andClaude Sonnet 5.5 82c5fe2e42 cos tests: keep the whole-real property inside the integer range
i32::MIN is outside Annex C's integer range and stays a real token (-2147483648.).
The generator now stops at -i32::MAX and a separate test pins the i32::MIN output.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 16:24:20 +01:00
vangel76andClaude Sonnet 5.5 a77cd9cb78 cos: reachable_objects keeps no parsed objects
full_save_order retained up to 64 MiB of parsed objects for the write pass, which
reachable_objects then dropped unused. It now asks for no retention.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 16:24:15 +01:00
vangel76andClaude Sonnet 5.5 8ac51f833b cos: charge the object-stream pre-scan to one budget per rebuild
Overlapping unterminated dictionaries could each be read up to 1 MiB. A shared
budget (the file length) is now taken off on every path; a spent budget keeps the
object a candidate, as before.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 16:21:28 +01:00
vangel76andClaude Sonnet 5.5 c9947e76e9 Merge upstream/main into perf/write-full-retain-parsed-objects
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 16:13:40 +01:00
FredApps 33e3945c04 Merge main and preserve unreadable diagnostics with cached failures 2026-11-07 15:48:09 +01:00
mtlcl410andClaude Opus 5.5 7de85d520c M1: a hybrid update without /Prev is rebuilt by the renderer as by cos (core.repair-broken-files)
The renderer (hayro-syntax (44)) counted the sections its chain reached, and
a hybrid section's /XRefStm stream (ISO 32000-2 §7.5.8.4) counted as one, so
a hybrid update without /Prev was rebuilt by cos but not by the renderer. It
now asks what cos asks: whether the newest section names an earlier one
through /Prev. The /XRefStm stream belongs to the same update.

Tests: a synthetic hybrid fixture (pdfcraft-testkit), with and without
/Prev, in cos and render.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-07 22:29:49 +08:00
mtlcl410andClaude Opus 5.5 6847b1ea7e M1: a file whose last update has no /Prev opens with all its pages and a repair note (core.repair-broken-files)
An update whose cross-reference section has no /Prev (ISO 32000-2 §7.5.6,
§7.5.8.2) cuts the earlier revisions, and the page tree with them, off the
chain. The signed PDFBOX-2720 and PDFBOX-2942 files have this shape: the
document model found no page tree and the renderer showed 2 blank pages
found by scanning for page dictionaries.

When the chain reaches only the newest section, the file holds another
startxref and the catalog's /Pages can't be read, both readers now rebuild
the table from the file's objects, as for a damaged file:

- cos: the existing reconstruction (newest in-file definition wins), a
  repair-log line saying why, and `Document::rebuilt_unlinked_update`.
- hayro-syntax (44): `root_xref` repairs the table before the page tree is
  read; `Pdf::rebuilt_unlinked_update` says so.
- render: an open warning, and the lazy structure reader accepts such a
  rebuilt file, so links, fields and bookmarks are read too.

A save rewrites the file in full (main's rule for reconstructed files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-07 22:22:47 +08: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 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
Grzegorz MartensandClaude Opus 5.5 69e26167e4 M2.6: a page's content is read with its /DecodeParms given by reference
`decode_with_damage` and `decode_contents_with_damage` read a stream's
`/DecodeParms` only when direct, as `Stream::decoded` does, so content behind
a predictor whose parameters, or a number in them, are an indirect object
decoded with the predictor's tag bytes left in, and was taken as whole. Edit
then listed and wrote back the wrong bytes, and pruning dropped an image such
a form draws. ISO 32000-2 lets those values be indirect.

- `decode_with_damage` takes the document and reads the parameters through
  it, and so does `decode_contents_with_damage`, and `decode_contents` with
  it: printing, accessibility's reading of a page and a page copied as a form
  read them too. Code that calls `decode` on a stream itself doesn't yet.
- It reads through `Document::decoding_view`: `with_direct_parms`, but a copy
  of the stream holds only its `/Filter` and the parameters, its data shared,
  so a stream a page's `/Contents` lists again and again isn't copied whole
  for each.

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 480fdb114a M2.6: Optimize leaves a page damaged partway as it is stored
Optimize read a page's content up to any damage in its streams as the whole
of it. Merging a page's image strips then wrote the page's content again as
one stream, without what followed the damage, and the image pass measured
each image by its uses before the damage only, so one drawn larger past it
could be resampled below that use.

- `DecodeBudget::decode_contents_with_damage` reads a page's `/Contents` as
  `decode_contents` does, each stream also saying whether it is damaged
  partway; `decode_contents` is it without the flag.
- `content_bytes` gives `None` for a page with a damaged stream, as for one
  that doesn't decode: the content is not all known. Its strips aren't merged
  (`merge_page` already leaves such a page alone), and no image is resampled
  (`effective_resolutions` gives every image `INFINITY`).
- A form damaged partway leaves the measuring incomplete in the same way.

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 712345d7a1 M2.6: an edit never rewrites a content stream from the part before its damage
A Flate stream damaged partway decodes to what came before the damage, and
`DecodeBudget::decode` gave that back as the stream's data: the decoder's
partial flag was dropped in `Stream::decoded_within_counted`. Editing a text
line, a block, an image or an added item in such a stream wrote it again from
that part, so everything after the damage was lost on save, though other
readers may still read it.

- `DecodeBudget::decode_with_damage` decodes as `decode_within` does and says
  whether the stream is damaged partway. A missing checksum or end-of-data
  marker is no damage, as before.
- Edit reads the page's streams with it (`ContentStream`), and
  `rewritten_contents` refuses, before adding anything, a change to a damaged
  stream. Changes in the page's other streams go on, and the damaged one stays
  as stored.
- Selecting objects (`editable_objects`, moving added items) refuses a page
  with a damaged stream, as it refuses one that doesn't decode: what follows
  the damage could change every later item's graphics state.
- Marking a tag as an artifact or a figure as decorative refuses when the
  tag's content is in a damaged stream (`ArtifactError::Damaged`,
  `AltError::Damaged`), and changes nothing, the structure tree included.

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 8fabc86f3a Merge upstream/main into perf/write-full-retain-parsed-objects
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 11:52:43 +01:00
mtlcl410andClaude Opus 5.5 d4b4218cf1 M2: a fax image with more /Rows than its /Height is decoded to its height (hayro-syntax patch 41)
A CCITTFaxDecode image whose /DecodeParms /Rows exceeds the image's
/Height (PDFBOX-771: /Rows 1084975104 on 2,592 columns) asked the decoder
for /Rows rows; the size check refused that, and every page drew blank.
The image is /Height rows (ISO 32000-2 Table 87), so the decoder is asked
for those; /Rows still counts for a stream decoded without an image's
height. pdfcraft-cos already decodes /Height rows; a test pins it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-07 11:55:32 +08:00
mtlcl410andClaude Opus 5.5 e6c399811d fix(render): an image whose PNG predictor was declared but never applied draws its samples (hayro-syntax patch 40)
A writer declared /Predictor 15 on an image and its soft mask without
applying it, so the inflated data is exactly /Height rows of the row length
with no filter-type byte before each (ISO 32000-2 §7.4.4.4 requires one per
PNG-predicted row). Read with the predictor, every row's first sample was
taken for a filter type, the image came out a row short and shifted, and the
page drew it in fragments; image export said its data was shorter than its
size. PDFium, MuPDF and Poppler read such data as the samples it is.

Only that exact length takes the new path, and only for the last Flate
filter of an image whose height is known; anything else is predicted as
before. The renderer (vendored hayro-syntax, patch 40) reports it with
warn!, like its other lenient filter decodes; pdfcraft-filters returns it as
tolerated damage (partial in tolerant decoding, an error in strict), through
the new decode_image_tolerant_within that pdfcraft-cos uses for images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-07 11:05:20 +08:00
mtlcl410 ea54453ad2 Merge remote-tracking branch 'upstream/main' into pr-6214
# Conflicts:
#	crates/cos/src/lib.rs
2026-11-06 19:11:57 +08:00
KenM76andClaude Opus 5.5 1107834209 Merge origin/main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-06 06:03:59 -05:00
mtlcl410 18964e0086 Merge remote-tracking branch 'upstream/main' into pr-6312
# Conflicts:
#	crates/cos/src/writer.rs
2026-11-06 18:59:34 +08:00
KenM76andClaude Opus 5.5 80ab49d09a Merge origin/main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-06 05:59:18 -05:00