Commit Graph
492 Commits
Author SHA1 Message Date
Michael Higgins f51ce3cfcc fix(render): bound the objects lopdf's fallback parses from object streams and arrays (#5083)
## What this changes

lopdf's fallback loader (used by `inspect` when the lazy reader fails, and always by `attachment_data`) parsed every object stream eagerly, capping only decoded bytes. A stream of `0 0 0 …` inside the 256 MiB limit built ~120M `Object`s (10+ GB) and aborted. This bounds what it builds:

- **Per top-level object:** at most 4,194,304 objects (`parser::MAX_OBJECT_NODES`, the same as cos's `MAX_NODES`), counting every array element and dictionary value at any depth. One budget is threaded through `array` / `_dictionary` / `_direct_object`. An object is charged once it is built, so failed `alt` branches cost nothing, and an exhausted budget is a nom `Failure`.
- **Per object stream:** at most 1,000,000 index entries (cos's `/N` clamp), and the index list is no longer allocated from the whole block.
- **Per load:** at most 8,388,608 objects across all object streams, in an atomic `NodeBudget` shared by rayon workers. A member reserves up to its own byte length (every object takes at least one byte) and refunds what it doesn't use, so parallel members can't hold the budget between them. Once the budget is spent, the remaining members are skipped with a warning.
- **`convert` in `crates/render/src/structure.rs`:** the depth limit is now 128, one level per array or dictionary, matching cos's parser. Before, it was 100 with two levels per dictionary, so ~51 nested dictionaries alone forced the lopdf fallback.

Logged as lopdf patch (7) in `vendor/README.md`.

Fixes #5083

## How you verified it

`cargo fmt --check` on `pdfcraft-render` and `cargo clippy -p pdfcraft-render --all-targets -- -D warnings` (which compiles the vendored lopdf). Regression tests in `crates/render/src/inspect/tests.rs`, where lopdf patches are tested in-tree (the vendored lopdf's own tests need its missing `assets/example.pdf`):

- `object_stream_members_over_the_node_cap_are_skipped` (croquete1): a member that is one array of 4.3M elements is skipped at the per-object cap; a small member is kept.
- `object_stream_index_entries_past_the_clamp_are_ignored`: of 1,000,001 distinct members, the one past the 1,000,000-entry clamp is never built.
- `object_streams_of_one_load_share_their_node_budget`, `object_streams_loaded_in_parallel_share_their_node_budget` and `a_malformed_object_stream_member_refunds_its_reservation` (Harrison-Booth): a small shared `NodeBudget` bounds the members of several streams, serially and across threads, and a malformed member refunds what it didn't build. `NodeBudget` and `ObjectStream::new_within` are now public for this.

- [x] `cargo fmt --check`
- [x] clippy with `-D warnings` on the crates I changed
- [x] tests pass on the crates I changed (`cargo test -p pdfcraft-render --lib object_stream`: 9 passed, including the five new tests)

## Never-crash

- [x] No new `unwrap`/`expect`/`panic!`/`unreachable!`/`todo!` outside tests
- [x] No indexing or slicing with input-derived positions; the budget uses `checked_sub` and a `min`-bounded reservation; index entries and objects built are capped; nesting stays bounded (128 in `convert`)
- [x] No `unsafe`
- [x] The abort this fixes has synthetic regression tests (above)

## Provenance

- [x] No assets, fonts or copied code; the limits follow `pdfcraft_cos`'s own caps

## Security

- [x] No change to `--root`, MCP, the control channel or network access

## Parity and reach

- Not applicable: an internal robustness fix to the fallback loader, with no user-facing feature or parity entry. The vendored-patch log (`vendor/README.md`, lopdf (7)) is updated.

## Anything a reviewer should look at first

- `vendor/README.md` now lists the tests above for patch (7).
- Ordinary (not compressed) indirect objects are capped at 4M objects each but aren't charged to the per-load budget; their total is bounded only by file size.
- `get_compressed_object` still decodes and re-parses the whole stream per lookup (pre-existing); it now gets one object's budget.
- Objects nested 101–128 deep now reach the Inspector through the lazy reader rather than being skipped by the fallback. The Inspector's own walkers cap depth at 32 or less.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Added limits to PDF object parsing and object-stream processing. Loading now parses only selected members, while lookups parse only the requested member. Members that exceed available limits are skipped in a deterministic order.
  * Increased the supported nesting depth for dictionary and object conversion, allowing more deeply nested content to be processed.
  * Documented that conversion failures can trigger eager loading as a fallback.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-11 07:48:14 +01:00
Perchenko Anton d47adeff89 M4: bind property drafts with edit-session and replay UI scenarios (#4594)
Binds Properties dialog drafts to the vendored edit-session crate (byte-identical to PerchAnton/edit-session@033f7c3a, ledger in vendor/README) and adds replayable UI scenarios. Includes a merge of main and a test covering the clean Properties inspection-tab live refresh.
2026-11-10 20:22:58 +01:00
FredApps e03d5a1c29 Render: an isolated CMYK group of any size blends in CMYK, in bands (#6885)
* Render: an isolated CMYK group of any size blends in CMYK, in bands

hayro patch (27) blended an isolated DeviceCMYK transparency group in CMYK only up to 2^24
device pixels (a 4096 x 4096 square); a larger one was blended in RGB. Print as image renders a
whole page at once, up to 8192 px on the long side, so a page enlarged to 11 x 17 in at 600 dpi
(about 43 MP) blended any group covering most of it in RGB, and a print test file's CMYK blend
patches drew their X's. The same applied to exporting a large page as an image.

A larger group is now blended in bands of whole rows, each within the old limit: both ink passes
and the tint conversion run per band, so their memory stays as before, and only the finished
group (4 bytes a pixel) is kept whole. Every band is blended before any is drawn, so a group a
pass has no tints for is still drawn as before, whole.

The regression test lowers the band to 7 rows (set_cmyk_band_pixels, hidden, per thread like
set_skip_offscreen_paths) and checks the banded group matches the whole one across an
anti-aliased edge, and is CMYK's green where magenta is under black with Difference.

* Test: CMYK green through the profile is (0, 166, 81); check green against RGB's magenta
2026-11-10 18:11:33 +01:00
bflatastic 3b23494513 Merge remote-tracking branch 'origin/main' into merge/egui-725a39fc-6699 2026-11-10 15:24:11 +07:00
Piohrun e2464e1440 M2: a JBIG2 mask with a large symbol dictionary draws, and one a decoder limit refuses takes its picture with it (#6722) (#6784) 2026-11-10 10:43:13 +07:00
bflatastic 70aa8e5c0c Merge #4459: M2: the standard 14 fonts fit the Euro to its standard width (view.render-pages)
# Conflicts:
#	vendor/README.md
2026-11-09 18:19:55 +07:00
bflatastic baa131de70 Merge #4433: M2: zero-area fills and clips mark the pixels they touch (view.render-pages)
# Conflicts:
#	vendor/README.md
#	vendor/hayro-interpret/src/interpret/path.rs
2026-11-09 18:19:48 +07:00
bflatastic 06a9385543 Merge #4410: fix(render): degenerate subpaths, all-zero dash arrays and negative line widths stroke as the spec says (hayro-interpret patch 61)
# Conflicts:
#	vendor/README.md
2026-11-09 18:19:14 +07:00
bflatastic 0dda66820c Merge #4396: M2: layers open in the state the view auto state sets, and the Layers panel follows /Order (view.layers-panel)
# Conflicts:
#	crates/render/src/inspect.rs
#	crates/render/src/lib.rs
#	vendor/README.md
2026-11-09 18:19:07 +07:00
bflatastic 15a264ff04 Merge #4393: M2: an Indexed image with a short lookup table or an out-of-range hival is drawn as most readers draw it, and one with no table is skipped with a warning (view.render-pages)
# Conflicts:
#	vendor/README.md
2026-11-09 18:17:39 +07:00
bflatastic 5257a6a34a Merge #3969: M10: a symbolic TrueType font without an encoding reads its glyph names (#3962)
# Conflicts:
#	vendor/README.md
2026-11-09 18:17:22 +07:00
mtlcl410 cf7734b17e Merge upstream/main into fix/standard14-euro-width 2026-11-09 19:07:08 +08:00
mtlcl410 5ac623ff9f Merge upstream/main into fix/zero-area-fill-and-clip 2026-11-09 19:05:11 +08:00
mtlcl410 299e263bf9 Merge upstream/main into fix/stroke-degenerate-and-dash 2026-11-09 19:02:50 +08:00
mtlcl410 ad42d5c276 Merge upstream/main into fix/indexed-short-lookup 2026-11-09 18:48:21 +08:00
mtlcl410 8d9f8d5f30 M5: a reply is not drawn on the page or flattened into it (comment.reply) (#5632) 2026-11-09 18:15:44 +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
alehelandClaude Opus 5.5 2be21c2e66 chore(deps): bump storytold/egui to 725a39fc
Moves the egui crates from 6d518fc7 to the tip of the fork's
migration/egui-037-20261024 line. It adds two patches:

- E22: the fallback JavaScript highlighter no longer colours
  TypeScript-only keywords (storytold/egui#24).
- E23: dragging a Resize corner keeps a direction that isn't
  resizable (storytold/egui#25).

The rest is lint and PATCHES.md only. Default fonts are unchanged,
so the egui-icons provenance link only moves to the new rev. The
vendor graph test and vendor/README.md follow the pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 10:51:30 +01:00
mtlcl410 f51eaa86e3 Merge main into fix/ocg-auto-state-view; renumber its patch to (103)
Main took hayro-interpret patch (76) for non-embedded Arial and Times New Roman
CIDs, so the View auto state becomes patch (103) in the ledger and the code
comments. The view.layers-panel note no longer lists the Print event's auto
state as missing: main applies it when printing (print.print-ready-pdf).
2026-11-09 11:50:10 +08:00
mtlcl410andClaude Opus 5.5 02db83e15d Merge upstream/main into fix/indexed-short-lookup
Main's #6663 added InterpreterWarning/RenderWarning::Type3WidthsMissing (patch
90) and a decoded-image cache that records warnings as flags (patch 91). Both
warning variants are kept in every match (renderer, automation, CLI), and
MalformedIndexedImage takes flag 32 in warning_bit/warnings_in. The ledger
keeps main's rows with (80) added; both sides' raster test modules are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 11:23:00 +08:00
mtlcl410andClaude Opus 5.5 d130c4f4de Merge upstream/main into fix/symbolic-truetype-glyph-names-3962
Main's #6569 (hayro-interpret patch 101) reads a symbolic TrueType font that
names no glyphs and keeps the Macintosh order as Mac OS Roman; this branch's
patch (79) reads one that names its glyphs by those names. Both are kept: the
renderer struct holds both fields, and the editor's unicode_map applies the
glyph-name reading only to a font whose post table names glyphs, then the Mac
OS Roman one, so each font takes the reading the renderer gives it. The ledger
keeps main's rows with (79) added; the editor reuses descriptor_flags and
MAX_SFNT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 11:15:40 +08:00
mtlcl410andClaude Opus 5.5 96e96e14f4 Merge upstream/main into fix/standard14-euro-width
Keeps main's vendor ledger (now with patches 82-102) with hayro-interpret
patch (77) added, and both sides' render test modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 11:10:04 +08:00
mtlcl410andClaude Opus 5.5 59461ba3cd Merge upstream/main into fix/zero-area-fill-and-clip
Keeps main's vendor ledger (now with patches 82-102) with hayro-interpret
patch (65) added, and both sides' render test modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 11:06:44 +08:00
mtlcl410andClaude Opus 5.5 0d42620d0f Merge upstream/main into fix/stroke-degenerate-and-dash
Keeps main's vendor ledger (now with patches 82-102) and adds hayro-interpret
patch (61) to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 11:04:55 +08:00
BFlat cde331fb7e Merge pull request #6663 from storytold/merge/render-interpret-patches
Land render and font patches (#6292, #6287, #6314, #6353, #6359, #6368, #6409, #6491, #6545, #6516, #6521, #6528, #6533, #6529, #6542, #6543, #6569, #6572)
2026-11-09 10:54:50 +08:00
mtlcl410andClaude Opus 5.5 b3e21261e8 Merge upstream/main into fix/standard14-euro-width
Keeps main's vendor ledger with hayro-interpret patch (77) added, and both
sides' render test modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:50:52 +08:00
mtlcl410andClaude Opus 5.5 e75384ea90 Merge upstream/main into fix/zero-area-fill-and-clip
Keeps main's vendor ledger with hayro-interpret patch (65) added, and main's
BlendMode import beside the patch's kurbo imports in path.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:47:14 +08:00
mtlcl410andClaude Opus 5.5 c615bd8224 Merge upstream/main into fix/stroke-degenerate-and-dash
Keeps main's vendor ledger with hayro-interpret patch (61) added, main's
BlendMode import beside the patch's Point and Cow in path.rs, and both sides'
stroke test modules (main's stroke_degenerate_cubic and stroke_zero_end_tangent
fix vello_common's stroker; this patch shapes subpaths in hayro-interpret).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:45:00 +08:00
mtlcl410andClaude Opus 5.5 f49ea0f70c Merge upstream/main into fix/indexed-short-lookup
Keeps main's vendor ledger with hayro-interpret patch (80) added, and both
sides' test modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:42:14 +08:00
mtlcl410andClaude Opus 5.5 45c7222704 Merge upstream/main into fix/symbolic-truetype-glyph-names-3962
Keeps main's vendor ledger and adds hayro-interpret patch (79) to it; the
symbolic TrueType program is read through doc.with_direct_parms, as main now
reads every font stream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:34:14 +08:00
mtlcl410andClaude Opus 5.5 7a8d3a2373 Merge main into fix/ocg-auto-state-view
crates/engine/src/print_raster.rs: the print raster keeps `for_print` (the View
auto state is not applied when printing) and main's Print-event layer states.
vendor/README.md: hayro-interpret patch (76) and its tests follow main's new
patch (89); main's hayro (31) and hayro-jbig2 (17) entries are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:19:59 +08:00
Luigi Pinna 8c7ea6377e test(content): the #2456 tests use a filtered inline image (#2483) 2026-11-09 10:13:54 +08:00
MarekSpodBiedry 344ba9f8d7 Refuse a legacy encryption key length above 128 bits instead of panicking (#1769) (#1882) 2026-11-09 10:13:00 +08:00
jonathanwxh-cell 5e671ec301 fix(render): treat combined fill-and-stroke operators as a knockout group (#3229) (#4788) 2026-11-09 10:09:06 +08:00
bflatastic b21ee3a710 Decode a soft mask that isn't averaged down within its image's decode
Patch 82 sent every soft mask through the decoded-image cache, so a mask that patch 91 keeps failed inside its image's entry got a second entry and counted as another decode. Only a mask averaged down needs an entry of its own; any other decodes as it did before patch 82.
2026-11-09 09:05:11 +07:00
bflatastic 9e6854869b Merge origin/main into merge/render-interpret-patches
# Conflicts:
#	crates/render/src/raster/tests.rs
#	crates/render/src/text/tests.rs
#	parity/acrobat-features.toml
#	vendor/README.md
2026-11-09 09:03:56 +07:00
bflatastic e49055053f Give the Type 3 widths warning a flag in the decoded image cache's warning set
Patch 90 adds InterpreterWarning::Type3WidthsMissing and patch 91 matches every warning to a flag, so the merged match needs the new variant.
2026-11-09 08:15:43 +07:00
bflatastic d2619333e9 Merge #6438: M2: a page past the rasterizer's coverage renders what fitted instead of failing (view.render-error-isolation)
# Conflicts:
#	vendor/README.md
2026-11-09 08:15:14 +07:00
bflatastic 795f9a9508 Merge #6532: M2: a CMap's bfchar mappings read codes at their codespace length (view.render-cid-fonts)
# Conflicts:
#	crates/render/src/raster/tests.rs
#	vendor/README.md
2026-11-09 08:14:19 +07:00
bflatastic e2f65dbf5a Merge #6572: M3: Find matches words on a vertical line (view.find)
# Conflicts:
#	vendor/README.md
2026-11-09 08:13:59 +07:00
bflatastic 8eb14f3892 Merge #6569: fix(render): a symbolic TrueType font in the Macintosh glyph order reads its codes as Mac Roman (hayro-interpret patch 101)
# Conflicts:
#	crates/render/src/raster/tests.rs
#	vendor/README.md
2026-11-09 08:13:36 +07:00
bflatastic d5ea69db50 Merge #6348: fix(render): a ToUnicode CMap reads its mappings past a token it can't parse (hayro-cmap patch 3)
# Conflicts:
#	crates/render/src/raster/tests.rs
#	vendor/README.md
2026-11-09 08:13:19 +07:00
bflatastic 5da76a640c Merge #6543: fix(render): a numbered glyph name at its own code reads as that code's character (hayro-interpret patch 100)
# Conflicts:
#	vendor/README.md
#	vendor/hayro-interpret/src/font/mod.rs
#	vendor/hayro-interpret/src/font/type3.rs
2026-11-09 08:13:01 +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 ec0330ea77 Merge #6542: fix(render): a patch mesh painted through a path its own size draws every patch (hayro-interpret patch 99)
# Conflicts:
#	vendor/README.md
2026-11-09 08:11:40 +07:00
bflatastic 183c66ddb5 Merge #6529: fix(render): a non-embedded SymbolMT draws Symbol's glyphs, whatever encoding it declares (hayro-interpret patch 98)
# Conflicts:
#	vendor/README.md
2026-11-09 08:11:26 +07:00
bflatastic 6160819a57 Merge #6533: M2: a font without a /Subtype is drawn by its /BaseFont (view.render-pages)
# Conflicts:
#	vendor/README.md
2026-11-09 08:11:11 +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 e2502b2f9b Merge #6528: fix(render): a CID-keyed CFF font with an emptied font dict still draws its other glyphs (hayro-interpret patch 93)
# Conflicts:
#	vendor/README.md
2026-11-09 08:10:53 +07:00