Repository navigation
fix(tables): read tables ruled with hairline fills, with row-group labels and merged cell runs - #512
yilmazhasan wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
1 issue found across 5 files
Confidence score: 3/5
- In
src/tables/detect_rects.rs, a split run can add the same original index more than once to a cell, so callers may see duplicate items. Keepitem_indicesunique per cell.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/tables/detect_rects.rs">
<violation number="1" location="src/tables/detect_rects.rs:1862">
P2: A split run now contributes its original index `idx` once per fragment that lands in a cell, so `item_indices` can hold the same index multiple times. `assign_items_to_grid`'s contract says it returns "the original item indices that were consumed", and the rect-path callers (e.g. lines 1679, 2321, 3316, 3629) build `Table::new(..., item_indices)` straight from this list without dedup, unlike the detect_lines callers which dedup at lines 1037-1038. Duplicate entries inflate `item_indices.len()`, which is used as positive evidence in `table_evidence_score` (detect_lines.rs:1086, `item_indices.len() * 100`) and as a tie-breaker (line 1097), so one split run can over-count a table's claimed items. Push `idx` into `indices` only once per split run (e.g. track whether any fragment of the run was placed, then push once).</violation>
</file>
Shadow auto-approve: would not auto-approve because issues were found.
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Confidence score: 5/5
- The comments in
tests/integration_tests.rssay these tests check table headers, but they assert data rows instead. Update the comments to match what the tests cover.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/integration_tests.rs">
<violation number="1" location="tests/integration_tests.rs:1360">
P3: The doc comment on `test_custom_table_first_row` and `test_body_before_table` says "Whether the table header is rendered correctly", but neither tests a table header: the first asserts a data row and the second asserts a body paragraph before the table. This was copy-pasted from `test_custom_table_header` and misleads readers about what each test guards. Give each test a doc comment that matches what it asserts.</violation>
</file>
Shadow auto-approve: would not auto-approve because issues were found.
Fix all with cubic | Re-trigger cubic
There was a problem hiding this comment.
Devin Review found 4 potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
There was a problem hiding this comment.
All reported issues were addressed across 10 files
Shadow auto-approve: would not auto-approve because issues were found.
Reply to a comment to ask cubic a question or push back. It learns from your replies.
Click Fix with cubic on a comment, or reply @cubic-dev-ai fix this, and cubic writes the fix.
View guided diff | Re-trigger cubic
…bels and merged cell runs Some generators draw every table rule as a thin filled rectangle instead of a stroke. Those slivers are too small for rect clustering and never reach line-based detection, so the grid is left to the heuristic pass and rows and columns come out broken (firecrawl#353). - Synthesize line segments from hairline fills for the line-based pass, welding per-cell slivers into rules. Only a real grid (>=3 horizontal and >=2 vertical rules of table length) is returned, so stray underlines cannot fabricate a table and horizontal-only layouts keep their per-cell segments for the later thin-rect fallback. - Ignore horizontal rules more than one row pitch outside the vertical rules' span: running-header and footer rules no longer stretch the table over the prose above it. - Cut a text run where a drawn column rule crosses its baseline, at the nearest word space, so two cells painted close enough to merge into one run land in their own columns. Fragments map back to their original item index once. - Place a rotated row-group label wholly inside the grid into the merged cell its column's rules describe, rejecting labels that cross a ruled interior row edge. - Keep row-group entries (empty first column, same filled columns as the entry above) from merging as continuations on both continuation paths, while wrapped tails still merge. Fixes firecrawl#353 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0b3b90e to
dde9f03
Compare
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Shadow auto-approve: would not auto-approve because issues were found.
View guided diff | Re-trigger cubic
- Widen the ruled span by the median rule gap rather than the largest, so one tall merged row cannot re-admit page furniture rules. - Test a column rule against the run's baseline instead of its box midpoint, so rules ending at the baseline still cut merged cell runs. - Recognize a three-row group (two blank first cells of three) as a row-group label column; larger tables keep the 3/4 ratio, since a label column broken up by wrapped tails sits near 2/3. - Keep two-column row-group entries apart: a single filled entry cell is enough, lowercase wrapped tails still merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Shadow auto-approve: would not auto-approve because issues were found.
Tip: Review your code locally with the cubic CLI to iterate faster.
View guided diff | Re-trigger cubic
`is_upright()` admits runs up to 45° off horizontal, but an oblique run's box edge only approximates its baseline and its characters do not advance along +x, so a vertical rule could cut it at an unrelated word space. Skip any run that is not level before comparing the rule with the baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Shadow auto-approve: would require human review. Hairline-fill rule synthesis, row-group label placement, run splitting, and row-merge heuristics with snapshots/tests. Unresolved dismissed findings on rotated-label capture, endpoint preservation, duplicate indices, and a stray footer require human sign-off.
View guided diff | Re-trigger cubic
Fixes #353
Some generators draw every table rule as a thin filled rectangle instead of a stroke. Those slivers are too small for rect clustering and never reach line-based detection, so the table falls to the heuristic pass and its rows and columns come out broken.
Changes
synthesize_lines_from_thin_rects): thin fills become line segments for the line-based pass, with per-cell slivers welded into rules. It only returns lines when they form a grid (≥3 horizontal and ≥2 vertical rules of table length). Stray underlines can't fabricate a table, and horizontal-only layouts keep their per-cell segment endpoints for the existing thin-rect fallback."…corpuscular MCHC"). The cut is limited to the line-based path and to drawn rules. Fragments map back to their item index once.Review feedback addressed
assign_items_to_gridwas removed. It depended on an extraction quirk that fix(extractor): keep word spaces painted as squeezed whitespace runs #517 removed, and it also applied to inferred column edges. It's replaced by the rule-crossing cut above, sodetect_rects.rsis unchanged.item_indicesfrom split runs are fixed: fragments are deduplicated back to one original index.yesare fine), and the guard coversis_wrapped_continuationtoo.test_snapshot_*per fixture with accurate doc comments, the order assertion compares positions, and the redundant prefix check is gone. Unit tests were added for the row-group heuristic (positive and wrapped-tail negative), the run split, synthesis welding, the grid gate, and the weld.Second round:
nexo-price-en, whose blank-first-cell rows are wrapped tails.Not changed: the trailing
1-3chapter-page footer in the second snapshot. StrippingN-Nlines is a separate postprocess change, and a standalone range like that is ambiguous, so I'd rather not fold it into this PR.Verification
cargo fmt,cargo clippy -- -D warningsandcargo testpass.main. Besides the two new fixtures, onlytd9264changes: in its three-column cross-reference table (section / remove / add), four references that were merged into one cell now get one row each, as they appear in the PDF. That table's other two columns are still lost, as onmain.pdf-evals(bench.py test) has not been run yet; it isn't available in my environment.🤖 Generated with Claude Code