Skip to content

fix(render): clip extracted text to the page CropBox (#2670) - #2838

Open
jonathanwxh-cell wants to merge 2 commits into
storytold:mainfrom
jonathanwxh-cell:fix/cropbox-text-clipping
Open

jonathanwxh-cell wants to merge 2 commits into
storytold:mainfrom
jonathanwxh-cell:fix/cropbox-text-clipping

Conversation

@jonathanwxh-cell

Copy link
Copy Markdown
Contributor

What this changes

Part of #2670 (item 5: Text outside the CropBox).

When a page's CropBox is smaller than its MediaBox, hayro::render_into clips visual rendering to page.intersected_crop_box() transformed by page.initial_transform(true), but TextDevice recorded every glyph drawn anywhere on the page without checking whether its view-space bounding box intersected the visible crop region. As a result, pdfcraft-cli text, text_extract, text_search, and the UI text layer extracted and matched hidden text outside the CropBox.

  • Store the view-space visible page box (initial.transform_rect_bbox(intersected_crop_box)) on TextDevice in extract_page_filtered.
  • In TextDevice::draw_glyph, skip glyphs whose view-space bounding box has no positive overlap with self.page (b.x0.max(self.page.x0) >= b.x1.min(self.page.x1) || b.y0.max(self.page.y0) >= b.y1.min(self.page.y1)), while keeping glyphs that lie inside or straddle the CropBox boundary.

How you verified it

  • cargo fmt --all -- --check
  • cargo clippy -p pdfcraft-render -p pdfcraft-cli --all-targets -- -D warnings
  • cargo test -p pdfcraft-render --lib text::
  • cargo test -p pdfcraft-cli --test text
  • cargo xtask layers
  • cargo xtask assets
  • Added unit test text::tests::text_outside_the_crop_box_is_clipped in crates/render/src/text.rs covering text and /ActualText inside vs outside all four CropBox edges, glyphs straddling the CropBox edge, rotations (0, 90, 180, 270), /UserUnit (1, 2), and zero-area/disjoint CropBoxes.
  • Added CLI integration test page_argument_tests::text_clips_glyphs_outside_the_crop_box in apps/pdfcraft-cli/tests/text.rs.

Never-crash

  • No unwrap() / expect() / panic!() / todo!() / unimplemented!() / unreachable!() added in library code.
  • Total floating-point bounds check on already-finite glyph and page rectangles.

Provenance

  • Clean-room implementation from PDF specification (CropBox clipping in ISO 32000-2 §7.7.3.3 / §14.11.2) and synthetic test fixtures.

jonathanwxh-cell and others added 2 commits October 16, 2026 18:51
The open worker can still be reading after the harness's four startup frames.
Removing the synthetic file then asserted zero tabs before opening completed.
A fixture-specific 300 ms delay reproduced the exact ui.rs:843 failure on
644f7ab; waiting by state passes under the same delay. Keep the file until
its tab opens, retaining the one-tab, filename and OS-quit assertions.
The diagnostic delay is removed. CropBox production code is unchanged.

Narrowly adapted from Guillem Hermida's OS-event test repair in upstream
storytold#2812 at
47e5398 (MIT OR Apache-2.0).
No other changes from that PR are included.

Validation: controlled red/green; final UI suite 52 passed, zero ignored;
CropBox render regression passed with its fully qualified exact test name;
strict UI Clippy all targets, formatting, layers, assets and wasm (31 crates).
The CLI CropBox acceptance test will also be verified in automatic exact-head CI.

Co-authored-by: Guillem Hermida <guillemhr2@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant