diff --git a/docs/installation.rst b/docs/installation.rst index f4c01d2a5..3bfd22dd1 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -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++ @@ -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 --------------------------------------------------------- diff --git a/src/__init__.py b/src/__init__.py index ba2e9e7d9..fbcae3c44 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -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): @@ -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 diff --git a/src/utils.py b/src/utils.py index 1dfb9ded8..882aa05ff 100644 --- a/src/utils.py +++ b/src/utils.py @@ -106,7 +106,7 @@ def write_text( def show_pdf_page( page, rect, - src, + docsrc, pno=0, keep_proportion=True, overlay=True, @@ -114,11 +114,11 @@ def show_pdf_page( 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 @@ -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") @@ -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") @@ -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: diff --git a/tests/test_4466.pdf b/tests/test_4466.pdf new file mode 100644 index 000000000..782c1bead Binary files /dev/null and b/tests/test_4466.pdf differ diff --git a/tests/test_annots.py b/tests/test_annots.py index bac2d9c68..556c9112e 100644 --- a/tests/test_annots.py +++ b/tests/test_annots.py @@ -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 @@ -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) diff --git a/tests/test_general.py b/tests/test_general.py index 62a634fe1..a3236d389 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -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.