Skip to content
Merged

Jules #4473

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
10 changes: 9 additions & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Problems after installation

* On Windows, Python error::

ImportError: DLL load failed while importing _fitz
ImportError: DLL load failed while importing _extra

This has been occasionally seen if `MSVCP140.dll` is missing, and appears
to be caused by a bug in some versions (2015-2017) of `Microsoft Visual C++
Expand Down Expand Up @@ -114,6 +114,14 @@ Problems after installation
This appears to be a problem in Jupyter labs; see:
https://github.com/pymupdf/PyMuPDF/issues/3643#issuecomment-2210588778.

* On Windows, Python error::

ImportError: dynamic module does not define module export function (PyInit__extra)

This was reported 2025-03-26 in https://github.com/pymupdf/PyMuPDF/issues/4405.

The fix appears to be to install the latest `VC_redist.x64.exe`.


Notes
---------------------------------------------------------
Expand Down
18 changes: 13 additions & 5 deletions src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,12 @@ def update(self,
cross_out: draw diagonal lines, 'Redact' only.
rotate: set rotation, 'FreeText' and some others.
"""
annot_obj = mupdf.pdf_annot_obj(self.this)

if border_color:
is_rich_text = mupdf.pdf_dict_get(annot_obj, PDF_NAME("RC"))
if not is_rich_text:
raise ValueError("cannot set border_color if rich_text is False")
Annot.update_timing_test()
CheckParent(self)
def color_string(cs, code):
Expand Down Expand Up @@ -10252,11 +10258,13 @@ def _pixmap_read_samples(pm, offset, n):
for i in range(n):
ret.append(mupdf.fz_samples_get(pm, offset+i))
return ret
sample0 = _pixmap_read_samples( pm, 0, n)
for offset in range( n, count, n):
sample = _pixmap_read_samples( pm, offset, n)
if sample != sample0:
return False
for offset in range( 0, count, n):
if offset == 0:
sample0 = _pixmap_read_samples( pm, 0, n)
else:
sample = _pixmap_read_samples( pm, offset, n)
if sample != sample0:
return False
return True

@property
Expand Down
16 changes: 8 additions & 8 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,19 @@ def write_text(
def show_pdf_page(
page,
rect,
src,
docsrc,
pno=0,
keep_proportion=True,
overlay=True,
oc=0,
rotate=0,
clip=None,
) -> int:
"""Show page number 'pno' of PDF 'src' in rectangle 'rect'.
"""Show page number 'pno' of PDF 'docsrc' in rectangle 'rect'.

Args:
rect: (rect-like) where to place the source image
src: (document) source PDF
docsrc: (document) source PDF
pno: (int) source page number
keep_proportion: (bool) do not change width-height-ratio
overlay: (bool) put in foreground
Expand Down Expand Up @@ -165,15 +165,15 @@ def calc_matrix(sr, tr, keep=True, rotate=0):
pymupdf.CheckParent(page)
doc = page.parent

if not doc.is_pdf or not src.is_pdf:
if not doc.is_pdf or not docsrc.is_pdf:
raise ValueError("is no PDF")

if rect.is_empty or rect.is_infinite:
raise ValueError("rect must be finite and not empty")

while pno < 0: # support negative page numbers
pno += src.page_count
src_page = src[pno] # load source page
pno += docsrc.page_count
src_page = docsrc[pno] # load source page
if src_page.get_contents() == []:
raise ValueError("nothing to show - source page empty")

Expand All @@ -199,7 +199,7 @@ def calc_matrix(sr, tr, keep=True, rotate=0):
i += 1
_imgname = n + str(i)

isrc = src._graft_id # used as key for graftmaps
isrc = docsrc._graft_id # used as key for graftmaps
if doc._graft_id == isrc:
raise ValueError("source document must not equal target")

Expand All @@ -210,7 +210,7 @@ def calc_matrix(sr, tr, keep=True, rotate=0):
doc.Graftmaps[isrc] = gmap

# take note of generated xref for automatic reuse
pno_id = (isrc, pno) # id of src[pno]
pno_id = (isrc, pno) # id of docsrc[pno]
xref = doc.ShownPages.get(pno_id, 0)

if overlay:
Expand Down
Binary file added tests/test_4466.pdf
Binary file not shown.
76 changes: 76 additions & 0 deletions tests/test_annots.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,12 +544,25 @@ def test_4254():
annot = page.add_freetext_annot(rect, "Test Annotation from minimal example")
annot.set_border(width=1, dashes=(3, 3))
annot.set_opacity(0.5)
try:
annot.set_colors(stroke=(1, 0, 0))
except ValueError as e:
assert 'cannot be used for FreeText annotations' in str(e), f'{e}'
else:
assert 0
annot.update()

rect = pymupdf.Rect(200, 200, 400, 400)
annot2 = page.add_freetext_annot(rect, "Test Annotation from minimal example pt 2")
annot2.set_border(width=1, dashes=(3, 3))
annot2.set_opacity(0.5)
try:
annot2.set_colors(stroke=(1, 0, 0))
except ValueError as e:
assert 'cannot be used for FreeText annotations' in str(e), f'{e}'
else:
assert 0
annot.update()
annot2.update()

# stores top color for each pixmap
Expand Down Expand Up @@ -611,3 +624,66 @@ def test_richtext():
annot.update(fill_color=gold, opacity=0.5, rotate=90)
pix2 = page.get_pixmap()
assert pix1.samples == pix2.samples


def test_4447():
document = pymupdf.open()

page = document.new_page()

text_color = (1, 0, 0)
fill_color = (0, 1, 0)
border_color = (0, 0, 1)

annot_rect = pymupdf.Rect(90.1, 486.73, 139.26, 499.46)

try:
annot = page.add_freetext_annot(
annot_rect,
"AETERM",
fontname="Arial",
fontsize=10,
text_color=text_color,
fill_color=fill_color,
border_color=border_color,
border_width=1,
)
except ValueError as e:
assert 'cannot set border_color if rich_text is False' in str(e), str(e)
else:
assert 0

try:
annot = page.add_freetext_annot(
(30, 400, 100, 450),
"Two",
fontname="Arial",
fontsize=10,
text_color=text_color,
fill_color=fill_color,
border_color=border_color,
border_width=1,
)
except ValueError as e:
assert 'cannot set border_color if rich_text is False' in str(e), str(e)
else:
assert 0

annot = page.add_freetext_annot(
(30, 500, 100, 550),
"Three",
fontname="Arial",
fontsize=10,
text_color=text_color,
border_width=1,
)
annot.update(text_color=text_color, fill_color=fill_color)
try:
annot.update(border_color=border_color)
except ValueError as e:
assert 'cannot set border_color if rich_text is False' in str(e), str(e)
else:
assert 0

path_out = os.path.normpath(f'{__file__}/../../tests/test_4447.pdf')
document.save(path_out)
9 changes: 9 additions & 0 deletions tests/test_general.py
Original file line number Diff line number Diff line change
Expand Up @@ -1685,3 +1685,12 @@ def test_4415():
pixmap.save(path_out)
rms = gentle_compare.pixmaps_rms(path_out_expected, path_out)
assert rms == 0, f'{rms=}'

def test_4466():
path = os.path.normpath(f'{__file__}/../../tests/test_4466.pdf')
with pymupdf.Document(path) as document:
for page in document:
print(f'{page=}', flush=1)
pixmap = page.get_pixmap(clip=(0, 0, 10, 10))
print(f'{pixmap.n=} {pixmap.size=} {pixmap.stride=} {pixmap.width=} {pixmap.height=} {pixmap.x=} {pixmap.y=}', flush=1)
pixmap.is_unicolor # Used to crash.