Repository navigation
Conversation
Some PDF producers fit long text into a table cell by clipping it to the cell's box (`q <cell> re W n ... Q`). The text layer keeps the whole string, whose hidden end runs under the next column, and the glyph rectangle alone cannot say which cell the text belongs to. `PdfTextCell.clip_bbox` holds the bounding box of the clip region that was in force when the text was painted, in the same coordinate origin as `rect`, or None when nothing clipped it. The origin conversions move it with the rectangle. docling-parse fills it from its clip state (docling-project/docling-parse#404); the table matcher in docling can then place a clipped word by its clip rather than its glyph box (docling-project/docling#4762). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Jeff Witt <1848307+wittjeff@users.noreply.github.com>
|
✅ DCO Check Passed Thanks @wittjeff, all your commits are properly signed off. 🎉 |
Merge Protections🔴 1 of 2 protections blocking · waiting on 👀 reviews
🔴 Require two reviewer for test updatesWaiting for
This rule is failing.When test data is updated, we require two reviewers
Show 1 satisfied protection🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
AI triage (advisory)Checked commit The PR adds an optional CI safety: no concern found. A maintainer must still approve CI. The |
There was a problem hiding this comment.
AI first review (advisory): blocker found
This review covers the full diff at b68744cd8f. It read the code and did not run it.
The new clip_bbox field is optional with a None default, so existing JSON still loads. The main risk is that one origin-conversion path in SegmentedPdfPage.get_cells_in_bbox does not move clip_bbox with rect. The PR says the two always share one origin. Check that path before merging.
Findings outside the diff lines
-
blocker
docling_core/types/doc/page.py:826:get_cells_in_bboxconvertsrectbut notclip_bbox
The field description saysclip_bboxis "in the same coordinate origin asrect". This PR addsPdfTextCell.to_top_left_origin()andto_bottom_left_origin()to keep the two boxes consistent.SegmentedPdfPage.get_cells_in_bboxdoes not call them. It assignspc.rectdirectly, in both the TOPLEFT branch and the BOTTOMLEFT branch (also at line 828).Trigger: a page has
PdfTextCells with aclip_bbox, and the caller passes abboxin the other origin.Result: the returned cell has a
rectin the origin ofbbox, but itsclip_bboxstays in the page origin. A consumer that compares the two boxes, for example withBoundingBox.get_intersection_bbox, gets wrong geometry or aValueError("BoundingBoxes have different CoordOrigin"). The consumer would do this to find which table cell a clipped string belongs to, which is the purpose of the field.Fix: call the new method on the copy. For example, use
pc.to_top_left_origin(self.dimension.height)andpc.to_bottom_left_origin(self.dimension.height).TextCellhas the same methods, so this works for both cell types. Add a test that callsget_cells_in_bboxwith a clipped cell and abboxin the other origin. The new tests cover only the direct method calls.
A maintainer makes the merge decision. This review never approves the pull request.
…bbox `get_cells_in_bbox` reassigned `rect` on the copy it returns, so a clipped cell came back with `rect` in the query's origin and `clip_bbox` still in the page's. Convert the copy through the cell's own origin methods instead; both `TextCell` and `PdfTextCell` have them, so mixed cell lists keep working. Adds a test that queries a clipped cell in each origin. Found by the advisory first review on docling-project#964. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Jeff Witt <1848307+wittjeff@users.noreply.github.com>
Summary
Companion to docling-project/docling-parse#443 (fixes docling-parse#404), for docling-project/docling#4762.
Some PDF producers fit long text into a table cell by clipping it to the cell's box (
q <cell> re W n ... Q). The text layer keeps the whole string, whose hidden end runs under the next column, and the glyph rectangle alone cannot say which cell the text belongs to. docling-parse now tracks that clip per glyph; this PR gives it a place to land on the model.Change
PdfTextCell.clip_bbox: BoundingBox | None = None: the bounding box of the clip region in force when the text was painted (ISO 32000-1, 8.5.4), in the same coordinate origin asrect;Nonewhen nothing clipped it.rectstays the full glyph box; the part of it outsideclip_bboxis not visible on the page. A word or line carries the box when all of its glyphs share it.to_top_left_origin()andto_bottom_left_origin()onPdfTextCellmove the box with the rectangle, so a consumer that flips the page origin keeps the two consistent.Two commits, rebased on
main, no merge commits: the model change, and a follow-up that makesSegmentedPdfPage.get_cells_in_bboxconvert the copy it returns through the cell's own origin methods, so a clipped cell comes back withrectandclip_bboxin the query's origin (the advisory first review caught that it reassignedrectalone). Squash on merge is fine. No schema version bump:PdfTextCellis not part ofDoclingDocument, the field is optional with aNonedefault, andcheck-compat-projectorspasses.Compatibility
test_legacy_payload_still_validates).exclude_none=Truethe field is absent from exports of cells that have no clip, so existing serialised pages do not change."clip_bbox" in PdfTextCell.model_fields), so the two PRs can land in either order; once this one is released, docling-parse can raise its docling-core floor and drop the guard.Tests
Seven tests in
tests/test_page.py::TestPdfTextCellClipBbox: default and export omission, legacy payload, JSON round trip, origin conversion with and without a clip, andget_cells_in_bboxwith a clipped cell queried in each origin.tests/test_page.pypasses (48 tests); ruff, the compat-projector check andtypass on the changed files.Downstream
clip_bbox, the table matcher can place it by that box instead of its glyph box; the docling-only fix in docling#4786 could not see the clip.🤖 Generated with Claude Code