Skip to content

Commit 7bb15c5

Browse files
committed
Fix several small correctness bugs
- Archive.add(): fix broken assert (missing comma turned the error message into part of the boolean expression, disabling the empty-name check for binary content). - Annot.file_info: avoid UnboundLocalError when a crafted/malformed attachment's /FS dict has neither /UF nor /F. - Page.set_language(): call pdfpage.obj() instead of passing the bound method itself, which broke every call that actually sets a language. - Widget.on_state(): skip None entries from button_states() instead of raising TypeError. - Pixmap.tobytes(): add missing f-string prefix so the alpha error message actually includes the format name. - JM_matrix_from_py(): fix copy-paste bug returning FzRect() instead of FzMatrix() on the float-conversion failure path. - utils.get_text(): remove a bare assert that made the documented "misspelled option falls back to text" behavior unreachable. - utils.get_label_pno(): return "" (as documented) instead of raising IndexError when no label rule applies to the given page number. - utils.get_sorted_text(): guard against ZeroDivisionError for zero-width glyph bboxes. - _apply_pages.py (method='fork'): send the actual page numbers to workers instead of plain list indices, which silently processed the wrong pages for any non-contiguous/non-zero-based `pages` list. - table.chars_in_rect(): compare against c["top"]/c["bottom"] (top-down, matching the rect from page.get_drawings()) instead of c["y0"]/c["y1"] (PDF-native bottom-up space), fixing table detection for frames drawn as filled rectangles.
1 parent 733b19d commit 7bb15c5

4 files changed

Lines changed: 26 additions & 20 deletions

File tree

‎src/__init__.py‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,7 @@ def file_info(self):
964964

965965
fs = mupdf.pdf_dict_get(annot_obj, PDF_NAME('FS'))
966966

967+
filename = None
967968
o = mupdf.pdf_dict_get(fs, PDF_NAME('UF'))
968969
if o.m_internal:
969970
filename = mupdf.pdf_to_text_string(o)
@@ -2066,7 +2067,7 @@ def make_subarch(entries, mount, fmt):
20662067
raise ValueError(f'Not a file or directory: {content!r}')
20672068

20682069
elif is_binary_data(content):
2069-
assert isinstance(path, str) and path != '' \
2070+
assert isinstance(path, str) and path != '', \
20702071
f'Need name for binary content, but {path=}.'
20712072
self._add_treeitem(content, path)
20722073
return make_subarch([path], None, 'tree')
@@ -9510,6 +9511,8 @@ def on_state(self):
95109511
if bstate is None:
95119512
bstate = dict()
95129513
for k in bstate.keys():
9514+
if bstate[k] is None:
9515+
continue
95139516
for v in bstate[k]:
95149517
if v != "Off":
95159518
return v
@@ -13680,7 +13683,7 @@ def set_language(self, language=None):
1368013683
lang = mupdf.fz_text_language_from_string(language)
1368113684
assert hasattr(mupdf, 'fz_string_from_text_language2')
1368213685
mupdf.pdf_dict_put_text_string(
13683-
pdfpage.obj,
13686+
pdfpage.obj(),
1368413687
PDF_NAME('Lang'),
1368513688
mupdf.fz_string_from_text_language2(lang)
1368613689
)
@@ -14785,7 +14788,7 @@ def tobytes(self, output="png", jpg_quality=95):
1478514788
if idx is None:
1478614789
raise ValueError(f"Image format {output} not in {tuple(valid_formats.keys())}")
1478714790
if self.alpha and idx in (2, 6, 7):
14788-
raise ValueError("'{output}' cannot have alpha")
14791+
raise ValueError(f"'{output}' cannot have alpha")
1478914792
if self.colorspace and self.colorspace.n > 3 and idx in (1, 2, 4):
1479014793
raise ValueError(f"unsupported colorspace for '{output}'")
1479114794
if idx == 7:
@@ -21609,7 +21612,7 @@ def JM_matrix_from_py(m):
2160921612
for i in range(6):
2161021613
a[i] = JM_FLOAT_ITEM(m, i)
2161121614
if a[i] is None:
21612-
return mupdf.FzRect()
21615+
return mupdf.FzMatrix()
2161321616
return mupdf.FzMatrix(a[0], a[1], a[2], a[3], a[4], a[5])
2161421617

2161521618

‎src/_apply_pages.py‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,12 @@ def childfn():
137137
while 1:
138138
if verbose:
139139
pymupdf.log(f'{os.getpid()=}: calling get().')
140-
page_num = queue_down.get()
140+
item = queue_down.get()
141141
if verbose:
142-
pymupdf.log(f'{os.getpid()=}: {page_num=}.')
143-
if page_num is None:
142+
pymupdf.log(f'{os.getpid()=}: {item=}.')
143+
if item is None:
144144
break
145+
index, page_num = item
145146
try:
146147
if not document:
147148
if stats:
@@ -172,9 +173,9 @@ def childfn():
172173
if verbose: pymupdf.log(f'{os.getpid()=}: exception {e=}')
173174
ret = e
174175
if verbose:
175-
pymupdf.log(f'{os.getpid()=}: sending {page_num=} {ret=}')
176-
177-
queue_up.put( (page_num, ret) )
176+
pymupdf.log(f'{os.getpid()=}: sending {index=} {ret=}')
177+
178+
queue_up.put( (index, ret) )
178179

179180
error = None
180181

@@ -206,24 +207,24 @@ def childfn():
206207
t = time.time()
207208
if verbose:
208209
pymupdf.log(f'Sending page numbers.')
209-
for page_num in range(len(pages)):
210-
queue_down.put(page_num)
210+
for index, page_num in enumerate(pages):
211+
queue_down.put((index, page_num))
211212
if stats:
212213
_stats_write(t, 'Send page numbers')
213214

214215
# Collect results. We give up if any worker sends an exception instead
215216
# of text, but this hasn't been tested.
216217
ret = [None] * len(pages)
217218
for i in range(len(pages)):
218-
page_num, text = queue_up.get()
219+
index, text = queue_up.get()
219220
if verbose:
220-
pymupdf.log(f'{page_num=} {len(text)=}')
221-
assert ret[page_num] is None
221+
pymupdf.log(f'{index=} {len(text)=}')
222+
assert ret[index] is None
222223
if isinstance(text, Exception):
223224
if not error:
224225
error = text
225226
break
226-
ret[page_num] = text
227+
ret[index] = text
227228

228229
# Close queue. This should cause exception in workers and terminate
229230
# them, but on macos-arm64 this does not seem to happen, so we also

‎src/table.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,15 @@ def rect_in_rect(inner, outer):
236236

237237
def chars_in_rect(CHARS, rect):
238238
"""Check whether any of the chars in CHAR are inside rectangle 'rect'."""
239+
# NB: 'rect' (e.g. from page.get_drawings()) and c["x0"]/c["x1"] are in
240+
# top-down page space; c["top"]/c["bottom"] are the top-down counterparts
241+
# of c["y0"]/c["y1"], which are in PDF-native (bottom-up) space instead.
239242
return any(
240243
1
241244
and rect[0] <= c["x0"]
242245
and c["x1"] <= rect[2]
243-
and rect[1] <= c["y0"]
244-
and rect[3] >= c["y1"]
246+
and rect[1] <= c["top"]
247+
and rect[3] >= c["bottom"]
245248
for c in CHARS
246249
)
247250

‎src/utils.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def line_text(clip, line):
204204
lrect |= r # update line bbox
205205
# convert distance to previous word to multiple spaces
206206
dist = max(
207-
int(round((r.x0 - x1) / r.width * len(t))),
207+
int(round((r.x0 - x1) / r.width * len(t))) if r.width else 0,
208208
0 if (x1 == clip.x0 or r.x0 <= x1) else 1,
209209
) # number of space characters
210210

@@ -491,7 +491,6 @@ def get_text(
491491
"blocks": pymupdf.TEXTFLAGS_BLOCKS,
492492
}
493493
option = option.lower()
494-
assert option in formats
495494
if option not in formats:
496495
option = "text"
497496
if flags is None:

0 commit comments

Comments
 (0)