Repository navigation
Description
Description of the bug
Description of the bug *
In src/__init__.py, JM_new_lineart_device_Device is the Python lineart device (subclassing SWIG's mupdf.FzDevice2) used by Page.get_cdrawings() when g_use_extra is disabled or fallback is active.
To enable Python callbacks from the C++ device, __init__ must activate virtual dispatch by calling self.use_virtual_<method>(). On lines 24400–24402, self.use_virtual_fill_text, self.use_virtual_stroke_text, and self.use_virtual_ignore_text are written without parentheses (). In contrast, JM_new_texttrace_device (lines 24474–24476) correctly invokes them with ().
Because these statements evaluate to uncalled method references:
- SWIG director virtual method hooks are never registered with the C++
FzDevice2lineart device. - MuPDF's rendering engine skips Python callbacks (
fill_text = jm_increase_seqno,stroke_text = jm_increase_seqno,ignore_text = jm_increase_seqno). self.seqnois never incremented when text operations occur between drawing operations. Consequently, vector drawing items (pathdictionaries) following text receive incorrect sequence numbers, breaking drawing sequence order and z-index determination.
How to reproduce the bug
Highlight in Code
src/__init__.py:24395-24405:
24395: self.use_virtual_clip_image_mask()
24396: self.use_virtual_clip_stroke_path()
24397: self.use_virtual_clip_stroke_text()
24398: self.use_virtual_clip_text()
24399:
24400: >>>>> self.use_virtual_fill_text # BUG: Missing call parentheses ()
24401: >>>>> self.use_virtual_stroke_text # BUG: Missing call parentheses ()
24402: >>>>> self.use_virtual_ignore_text # BUG: Missing call parentheses ()
24403:
24404: self.use_virtual_fill_shade()
24405: self.use_virtual_fill_image()Steps & Reproduction Code
import pymupdf
import inspect
# Inspect source of JM_new_lineart_device_Device.__init__:
lines, _ = inspect.getsourcelines(pymupdf.JM_new_lineart_device_Device.__init__)
uncalled = [
l.strip()
for l in lines
if l.strip() in (
"self.use_virtual_fill_text",
"self.use_virtual_stroke_text",
"self.use_virtual_ignore_text",
)
]
print("Uncalled virtual method statements:", uncalled)
assert len(uncalled) == 3
# When running a page with text and drawings with g_use_extra = False:
pymupdf.g_use_extra = False
doc = pymupdf.open()
page = doc.new_page()
page.draw_rect([10, 10, 100, 100], color=(1, 0, 0))
page.insert_text([50, 50], "Sample Text")
page.draw_rect([150, 150, 200, 200], color=(0, 1, 0))
drawings = page.get_cdrawings()
for d in drawings:
print("Drawing rect:", d["rect"], "seqno:", d["seqno"])Expected Behavior
use_virtual_fill_text(), use_virtual_stroke_text(), and use_virtual_ignore_text() should be called with () so that SWIG dispatches MuPDF's text callbacks to Python to advance self.seqno.
Actual Behavior
The statements evaluate to unused method objects, SWIG vtable interception is not enabled for text operations, and seqno fails to advance on text.
Suggested Fix
--- a/src/__init__.py
+++ b/src/__init__.py
@@ -24400,3 +24400,3 @@ class JM_new_lineart_device_Device(mupdf.FzDevice2):
- self.use_virtual_fill_text
- self.use_virtual_stroke_text
- self.use_virtual_ignore_text
+ self.use_virtual_fill_text()
+ self.use_virtual_stroke_text()
+ self.use_virtual_ignore_text()PyMuPDF version
2.1
Operating system
Windows
Python version
3.14
Activity
Fix #5238