Repository navigation
find_tables(): keep quad corrections on for the line-based finder (#5198) - #5213
thomas-villani wants to merge 5 commits into
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
80ad995 to
d6480df
Compare
|
I have read the CLA Document and I hereby sign the CLA |
…sed finder. find_tables() sets minimum glyph heights so every char on a line shares one top and bottom, but those only apply while quad corrections are on. Importing pymupdf4llm turns them off for the whole process (eagerly on `import pymupdf` in 2.0), and since pymupdf#5205 find_tables() itself turns them off before get_layout() whenever use_layout is set. The line-based finder then built its chars from exact glyph boxes: a "." sits lower than the digits around it, is grouped onto a line of its own, and "0.74" is extracted as "0 74\n.". Turn corrections on before make_chars() when no layout boxes are used. The layout path, which turns them off on purpose, is unchanged. tests/test_tables.py: test_5198(): new. Fixes pymupdf#5198. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d6480df to
87c293b
Compare
|
Approved. With corrections back on, the line-based finder matches the corrections-on output on my 168-page ParseBench sample, and the layout path is unchanged. |
|
@thomas-villani please resolve the conflicts with the base branch, so we can merge the PR. |
The conflict resolution in c9b463a kept only test_5198 at the end of tests/test_tables.py, which would remove both tests from main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for merging #5243. Since it changes what this PR is for, I re-checked both against current Also, I've pushed b4b97fe. My merge from main had dropped Setup. PyPI 2.1 wheel (MuPDF 1.28.6) with #5243 on real files. Number of split cells:
Why this PR still matters. On
With this PR, I'm not claiming that either reading of the vertical header is the correct one. The point is that importing a library shouldn't change the result. Two things I noticed about #5243, for whoever reviews table changes. These come from 2.1 with both patches, before
One thing neither PR touches. The default import pymupdf
d = pymupdf.open(); p = d.new_page(width=595, height=842)
for a, b in [((72,72),(472,72)), ((72,112),(472,112)), ((72,152),(472,152)),
((72,72),(72,152)), ((272,72),(272,152)), ((472,72),(472,152))]:
p.draw_line(a, b)
for t, pt in [("P for interaction",(80,100)), ("Cooking fuel type",(280,100)),
("0.74",(80,140)), ("12.5 mg",(280,140))]:
p.insert_text(pt, t, fontsize=11)
print(p.find_tables().tables[0].extract())
# [['P for interaction', '', 'Cooking fuel type'], ['0.74', '', '12.5 mg']]
print(p.find_tables(use_layout=False).tables[0].extract())
# [['P for interaction', 'Cooking fuel type'], ['0.74', '12.5 mg']]I can open a separate issue for it if that's more useful. The articles are open access on PMC (CC BY 4.0, except PMC9500000, which is CC BY-NC-ND): PMC12000001, PMC2000354, PMC10000026, PMC9500000. I'm happy to share the scan script or attach particular PDFs. |
|
@JorjMcKie the conflicts are resolved as of b4b97fe; GH shows the PR as mergeable, with only src/table.py (+9) and tests/test_tables.py (+41) changed against main. Ready whenever you are. |
Fixes #5198.
Cause
find_tables()callsTOOLS.set_small_glyph_heights(True)("we need minimum bboxes") so that every character on a line shares one top and bottom. That setting only takes effect while quad corrections are on:JM_char_quad()returnsch->quadunchanged as soon asg_skip_quad_correctionsis set, before it reaches the small-heights code. With corrections off, each character keeps its exact glyph box. A "." then sits about 7pt below the digits around it,extract_text()groups it onto a line of its own (y_tolerance3), and the gap it leaves becomes a space:"0.74"comes back as"0 74\n.".The line-based finder never sets the toggle itself, so its output depends on what the process last set it to:
pymupdf4llm/helpers/{pymupdf_rag,document_layout,multi_column}.pycallunset_quad_corrections(True)at import time. 2.0 imports pymupdf4llm at the end ofimport pymupdf, which is why every 2.0 install shows 2.0:Table.extract()moves the decimal point out of numbers ("0.74" comes back as "0 74", newline, ".") — regression from 1.28.2 #5198. Lazy Import of heavy weight modules #5205 defers that import, but any process that imports pymupdf4llm, including throughpymupdf.to_markdown(), still hits it.find_tables()itself setsunset_quad_corrections(True)beforeget_layout()wheneveruse_layoutis set. When layout then provides no boxes, the line-based finder runs with corrections still off.Change
Before
make_chars(), turn quad corrections on when no layout boxes are used. The layout path, which turns them off deliberately, is unchanged. The existingfinallyalready restores the caller's value.test_5198()sets the toggle as an import of pymupdf4llm does and checks the extracted cells, and thatfind_tables()leaves the toggle as it found it. It fails without the change.Checks
On the 2.0 wheel with this
table.py(2.0 plus #5205's four lines, the onlytable.pydifference between 2.0 andmain):tests/test_tables.py: 47 passed, includingtest_5198. Without the change it is the one failure.use_layout=False, toggle on as afterimport pymupdf4llm: the output is identical to 1.28.0 (90 tables on 62 pages, every bbox and every cell). Without the change, 85 cells have split decimals, 1,840 cells differ from 1.28.0, and 3 extra tables are found.main, so the layout path does not change.Not in this PR: the layout path
Turning corrections on for the layout path too fixes its cell text, but it changes detection: in
test_4017, rows merge (13 to 8), and intest_paths_param, "Header2" splits across two cells. That suggests the layout path's row segmentation relies on the exact glyph boxes. Its cell text has split decimals since layout support was added (reproduced on 1.28.2 withpymupdf.layoutimported), and the layout expectations intests/test_tables.pyupdated in 12e17c8 record that output:'2755 9\n.','269\n-'for -269,'13144%\n.'for 131.44%. Fixing it would need separate character boxes for structure and for text. #5198 has the details.🤖 Generated with Claude Code