Skip to content

Commit 804b0de

Browse files
committed
table: probe candidate tables against CHARS instead of get_textbox()
cells_to_tables()'s no-text filter passed the module-global TEXTPAGE to page.get_textbox(), but that global is never assigned anywhere -- its comment ('textpage for cell text extraction') suggests a global statement was lost at some point; make_chars() and find_tables() only ever bind locals of the same name. Every probe therefore built a fresh full-page TextPage and Python-walked all of its characters (JM_copy_rectangle), ~24ms per candidate table -- the largest single find_tables() cost after character extraction (~30% of wall time on a 503-page table corpus). The same characters are already available in CHARS in identical page space, so scan those instead (built lazily, only when a candidate survives the geometric checks), with the same strict-overlap rule as JM_rects_overlap() and the same whitespace-only rejection. Drop the now-unreferenced TEXTPAGE global. Measured on the 503-page corpus: every produced table's (bbox, cells) is hash-identical; find_tables() mean wall time drops 106.1 -> 75.9 ms/page (-28%).
1 parent 6923195 commit 804b0de

1 file changed

Lines changed: 23 additions & 8 deletions

File tree

src/table.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ def __setitem__(self, key, value):
163163

164164
EDGES = _TableStateList(_EDGES_VAR) # vector graphics from PyMuPDF
165165
CHARS = _TableStateList(_CHARS_VAR) # text characters from PyMuPDF
166-
TEXTPAGE = None # textpage for cell text extraction
167166
TEXT_BOLD = mupdf.FZ_STEXT_BOLD
168167
TEXT_STRIKEOUT = mupdf.FZ_STEXT_STRIKEOUT
169168
FLAGS = (
@@ -1540,7 +1539,28 @@ def bbox_to_corners(bbox) -> tuple:
15401539
tables.append(list(current_cells))
15411540

15421541
# PyMuPDF modification:
1543-
# Remove tables without text or having only 1 column
1542+
# Remove tables without text or having only 1 column.
1543+
# The text probe scans the current call's CHARS: the module-global
1544+
# TEXTPAGE it used to pass to page.get_textbox() was never assigned, so
1545+
# every probe built and walked a fresh full-page TextPage per candidate.
1546+
# The overlap test mirrors JM_rects_overlap() (strict), and a candidate
1547+
# counts as texty iff it overlaps a non-whitespace character.
1548+
text_bboxes = None
1549+
1550+
def has_text(r):
1551+
nonlocal text_bboxes
1552+
if text_bboxes is None: # built once, only if a candidate survives
1553+
text_bboxes = [
1554+
(c["x0"], c["top"], c["x1"], c["bottom"])
1555+
for c in CHARS
1556+
if c["text"] not in white_spaces
1557+
]
1558+
rx0, rtop, rx1, rbottom = r
1559+
for x0, top, x1, bottom in text_bboxes:
1560+
if x0 < rx1 and x1 > rx0 and top < rbottom and bottom > rtop:
1561+
return True
1562+
return False
1563+
15441564
for i in range(len(tables) - 1, -1, -1):
15451565
r = pymupdf.EMPTY_RECT()
15461566
x1_vals = set()
@@ -1552,12 +1572,7 @@ def bbox_to_corners(bbox) -> tuple:
15521572
if (
15531573
len(x1_vals) < 2
15541574
or len(x0_vals) < 2
1555-
or white_spaces.issuperset(
1556-
page.get_textbox(
1557-
r,
1558-
textpage=TEXTPAGE,
1559-
)
1560-
)
1575+
or not has_text(r)
15611576
):
15621577
del tables[i]
15631578

0 commit comments

Comments
 (0)