fix(pymupdf): unwrap table-cell line breaks so their words stay searchable - #1264
fix(pymupdf): unwrap table-cell line breaks so their words stay searchable#1264cbcoutinho wants to merge 2 commits into
Conversation
|
Claude finished @cbcoutinho's task in 3m 15s —— View job Review: PR #1264 — unwrap table-cell
|
Review nit on #1264. Each `<br>` was substituted independently, so "a<br><br>b" became "a b". Harmless for retrieval -- the words stay separated -- but the tidier output costs one regex, and it matches how `spreadsheet.py` already normalises whitespace inside a cell. Confined to the table rows being rewritten, so prose keeps its own spacing, and it still happens before `page_boundaries` are measured. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7d6d92d to
ba7dceb
Compare
|
Round-1 nit addressed in
3344 unit tests pass; ruff/ty green. Sonar reports no issues on this PR. |
ba7dceb to
cd1d64a
Compare
|
Round-2 finding addressed in
3348 unit tests pass; ruff/ty green. |
cd1d64a to
b314f61
Compare
|
Round-3 finding addressed in
3350 unit tests pass; ruff/ty green. |
b314f61 to
ac04416
Compare
|
CI caught a real one that I introduced in the last round — fixed in SonarCloud Measured on a single padded row before and after:
Clearly quadratic. The leading quantifier is now Added a timing guard on a 20k-space row (1s ceiling) — I checked it genuinely discriminates rather than passing either way: the previous form takes 4.4s on that input. 3351 unit tests pass; ruff/ty green. |
…hable A markdown table row cannot contain a newline, so pymupdf4llm encodes every line wrap *inside* a cell as a literal `<br>`. That marker records how wide the column happened to be, not what the document says, and it lands in the indexed text glued to the words on either side: a cell reading "ISO 27001" is embedded as "ISO<br>27001", which no search for "ISO 27001" can match. Narrow columns wrap constantly, so forms and questionnaires lose a large share of their searchable phrases. Measured on three table-heavy documents: 19, 60 and 8 affected rows. Replace the marker with a space, scoped to table rows -- prose that legitimately discusses the `<br>` tag keeps saying so. The rewrite happens before `page_boundaries` are measured, so the offsets `pdf_highlighter` and the chunker index with stay exact rather than sliding right of their words. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each `<br>` was substituted independently, so "a<br><br>b" became "a b". Collapsing those runs afterwards, however, operates on the whole line: a legitimate double space in an unrelated cell of the same row was eaten as collateral, and -- because the early-out is per *page* -- so was one in a row with no break at all. Two rounds of narrowing that filter still left the same-row case, so the two-step is gone. One regex matches a run of adjacent breaks plus the spaces around it and yields a single space, which can only ever rewrite the text it matched. The leading space is `?` rather than `*`. With `[ \t]*` the engine consumes a whole run of spaces, fails to find a `<br>` after it, and retries one character shorter -- quadratic in the run length, and a rendered table is mostly padding runs. Measured on one padded row: 0.06s at 2k spaces, 0.71s at 8k, 4.37s at 20k, against 0.001s for the bounded form (python:S8786). One optional character covers the common `word <br> word` case and cannot backtrack; a wider run before a break is simply left alone, which is the safer miss. Covered by the mixed-cell row, a break-free row sharing a page with one that has a break, a page with no breaks at all, and a timing guard on a 20k-space row that the previous form failed. (An earlier version of this message cited `spreadsheet.py` as precedent for the normalisation. That file does not exist at this point in the stack -- it arrives two commits later -- so the comparison was to code the reader cannot see.) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ac04416 to
1ee8064
Compare
|



A markdown table row cannot contain a newline, so pymupdf4llm encodes every
line wrap inside a cell as a literal
<br>. That marker records how widethe column happened to be, not what the document says, and it lands in the
indexed text glued to the words on either side: a cell reading "ISO 27001"
is embedded as "ISO
27001", which no search for "ISO 27001" can match.
Narrow columns wrap constantly, so forms and questionnaires lose a large
share of their searchable phrases. Measured on three table-heavy documents:
19, 60 and 8 affected rows.
Replace the marker with a space, scoped to table rows -- prose that
legitimately discusses the
<br>tag keeps saying so. The rewrite happensbefore
page_boundariesare measured, so the offsetspdf_highlighterandthe chunker index with stay exact rather than sliding right of their words.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Stack created with GitHub Stacks CLI • Give Feedback 💬
This PR was generated with the help of AI, and reviewed by a Human