Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ def extract_cells(textpage, cell, markdown=False):
A string with the text extracted from the cell.
"""
text = ""
cell_rect = pymupdf.Rect(cell)
for block in textpage.extractRAWDICT()["blocks"]:
if block["type"] != 0:
continue
Expand Down Expand Up @@ -388,12 +389,21 @@ def extract_cells(textpage, cell, markdown=False):
):
continue # skip spans outside cell

# only include chars with more than 50% bbox overlap
# only include chars with more than 50% bbox overlap.
# A char with an empty bbox (e.g. second character of an expanded
# ligature with quad corrections unset, or a zero-width combining mark)
# has no area; fall back to testing if its origin is inside the cell:
# issue #5185.
span_text = ""
for char in span["chars"]:
this_char = char["c"]
bbox = pymupdf.Rect(char["bbox"])
if abs(bbox & cell) > 0.5 * abs(bbox):
if bbox.is_empty:
in_cell = pymupdf.Point(char["origin"]) in cell_rect
else:
in_cell = abs(bbox & cell_rect) > 0.5 * abs(bbox)

if in_cell:
span_text += this_char
elif this_char in white_spaces:
span_text += " "
Expand Down
34 changes: 34 additions & 0 deletions tests/test_5185.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
Regression test for issue #5185: table cell extraction dropped characters with
an empty bbox, e.g. the second character of an expanded ligature when quad
corrections are unset ("fine" became "fne").
"""

from pymupdf.table import extract_cells


class _TextPage:
"""Minimal stand-in for a TextPage, providing extractRAWDICT() only."""

def __init__(self, chars):
bbox = (0, 0, 500, 30)
span = {"bbox": bbox, "flags": 0, "char_flags": 0, "chars": chars}
line = {"bbox": bbox, "dir": (1, 0), "spans": [span]}
self.rawdict = {"blocks": [{"type": 0, "bbox": bbox, "lines": [line]}]}

def extractRAWDICT(self):
return self.rawdict


def test_5185():
cell = (0, 0, 100, 30)
chars = [
{"c": "f", "bbox": (10, 10, 15, 20), "origin": (10, 18)},
# zero-width bbox of the expanded "fi" ligature, inside the cell
{"c": "i", "bbox": (15, 10, 15, 20), "origin": (15, 18)},
{"c": "n", "bbox": (15, 10, 20, 20), "origin": (15, 18)},
{"c": "e", "bbox": (20, 10, 25, 20), "origin": (20, 18)},
# zero-width char outside the cell must still be excluded
{"c": "x", "bbox": (150, 10, 150, 20), "origin": (150, 18)},
]
assert extract_cells(_TextPage(chars), cell) == "fine"
Loading