PyMuPDF 1.25.3__tar.gz → 1.25.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pymupdf-1.25.3 → pymupdf-1.25.5}/PKG-INFO +1 -1
- {pymupdf-1.25.3 → pymupdf-1.25.5}/changes.txt +46 -2
- {pymupdf-1.25.3 → pymupdf-1.25.5}/scripts/sysinstall.py +9 -2
- {pymupdf-1.25.3 → pymupdf-1.25.5}/scripts/test.py +77 -99
- {pymupdf-1.25.3 → pymupdf-1.25.5}/setup.py +97 -13
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/__init__.py +99 -45
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/_apply_pages.py +1 -1
- pymupdf-1.25.5/src/_wxcolors.py +562 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/extra.i +154 -33
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/fitz___init__.py +1 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/table.py +1 -2
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/utils.py +275 -625
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/fitz_old.i +11 -3
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-python.i +1 -1
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/utils.py +9 -1
- pymupdf-1.25.5/tests/resources/merge-form1.pdf +0 -0
- pymupdf-1.25.5/tests/resources/merge-form2.pdf +0 -0
- pymupdf-1.25.5/tests/resources/test_3886.pdf +0 -0
- pymupdf-1.25.5/tests/resources/test_3887.pdf +0 -0
- pymupdf-1.25.5/tests/resources/test_4079_after.pdf +0 -0
- pymupdf-1.25.5/tests/resources/test_4224.pdf +0 -0
- pymupdf-1.25.5/tests/resources/test_4263.pdf +0 -0
- pymupdf-1.25.5/tests/resources/test_4415.pdf +0 -0
- pymupdf-1.25.5/tests/resources/test_4415_out_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_annots.py +31 -12
- pymupdf-1.25.5/tests/test_barcode.py +63 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_codespell.py +3 -9
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_font.py +23 -2
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_general.py +94 -4
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_insertpdf.py +84 -7
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pixmap.py +52 -2
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pylint.py +1 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_story.py +3 -1
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_textbox.py +11 -1
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_textextract.py +6 -4
- {pymupdf-1.25.3 → pymupdf-1.25.5}/COPYING +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/README.md +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/READMEb.md +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/READMEd.md +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/pipcl.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/pyproject.toml +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/pytest.ini +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/scripts/gh_release.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/__main__.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/fitz_table.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/fitz_utils.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src/pymupdf.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/__init__.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/__main__.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/_config.h +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-annot.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-convert.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-defines.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-devices.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-fields.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-fileobj.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-geo-c.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-geo-py.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-globals.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-other.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-pdfinfo.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-pixmap.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-portfolio.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-select.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-stext.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-xobject.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/version.i +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/README.md +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/conftest.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/gentle_compare.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/001003ED.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/1.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/2.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/2201.00069.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/3.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/4.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/Bezier.epub +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/PragmaticaC.otf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/battery-file-22.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/bug1945.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/bug1971.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/chinese-tables.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/chinese-tables.pickle +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/circular-toc.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/cms-etc-filled.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/cython.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/cython.pickle +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/dotted-gridlines.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/full_toc.txt +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/github_sample.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/has-bad-fonts.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/image-file1.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/img-regular.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/img-transparent.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/img-transparent.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/interfield-calculation.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/joined.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/metadata.txt +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/mupdf_explored.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/nur-ruhig.jpg +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/quad-calc-0.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/simple_toc.txt +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/small-table.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/strict-yes-no.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/symbol-list.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/symbols.txt +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-2333.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-2462.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-2812.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3143.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3150.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3207.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3591.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3820.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-4055.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-707448.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-707673.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-E+A.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-linebreaks.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test2093.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test2182.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test2238.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_1645_expected.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_1824.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2108.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2270.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2533.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2548.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2553-2.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2553.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2596.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2608_expected +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2634.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2635.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2645_1.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2645_2.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2645_3.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2710.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2730.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2742.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2788.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2791_content.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2791_coverpage.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2861.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2871.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2885.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2904.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2907.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2954.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2957_1.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2957_2.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2969.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2979.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3050_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3058.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3062.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3070.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3072.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3087.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3179.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3186.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3197.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3357.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3362.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3376.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3448.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3448.pdf-expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3450.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3493.epub +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3569.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3594.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3615.epub +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3624.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3624_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3650.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3654.docx +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3677.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3687-3.epub +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3687.epub +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3705.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3725.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3727.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3780.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3789.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3842.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3848.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3854.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3854_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.0.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.1.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.2.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.3.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.4.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.5.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.6.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.7.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3933.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3950.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3994.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4004.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4017.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4026.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4034.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4043.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4047.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4079.pdf +0 -0
- /pymupdf-1.25.3/tests/resources/test_4079_after.pdf → /pymupdf-1.25.5/tests/resources/test_4079_after_1.25.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4090.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4125.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4139.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4141.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4147.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4179.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4179_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4180.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4180_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4182.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4182_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4245.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4245_expected.png +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_annot_file_info.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_delete_image.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_toc_count.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/text-find-ligatures.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/type3font.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/v110-changes.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/widgettest.pdf +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/run_compound.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2548.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2634.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2904.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2907.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_4141.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_badfonts.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_balance_count.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_cluster_drawings.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_crypting.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_docs_samples.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_drawings.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_embeddedfiles.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_extractimage.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_flake8.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_geometry.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_imagebbox.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_imagemasks.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_import.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_insertimage.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_linebreaks.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_linequad.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_memory.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_metadata.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_mupdf_regressions.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_named_links.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_nonpdf.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_object_manipulation.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_objectstreams.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_optional_content.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_page_links.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pagedelete.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pagelabels.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_remove-rotation.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_rtl.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_showpdfpage.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_spikes.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_tables.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_tesseract.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_textsearch.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_toc.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_widgets.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_word_delimiters.py +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/valgrind.supp +0 -0
- {pymupdf-1.25.3 → pymupdf-1.25.5}/wdev.py +0 -0
|
@@ -2,6 +2,43 @@ Change Log
|
|
|
2
2
|
==========
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
**Changes in version 1.25.5 (2025-03-31)**
|
|
6
|
+
|
|
7
|
+
* Fixed issues:
|
|
8
|
+
|
|
9
|
+
* **Fixed** `4372 <https://github.com/pymupdf/PyMuPDF/issues/4372>`_: Text insertion fails due to missing /Resources object
|
|
10
|
+
* **Fixed** `4400 <https://github.com/pymupdf/PyMuPDF/issues/4400>`_: Infinite loop in fill_textbox
|
|
11
|
+
* **Fixed** `4403 <https://github.com/pymupdf/PyMuPDF/issues/4403>`_: Unable to get_text() - layer/clip nesting too deep
|
|
12
|
+
* **Fixed** `4415 <https://github.com/pymupdf/PyMuPDF/issues/4415>`_: PDF page is mirrored, origin is at bottom-left
|
|
13
|
+
|
|
14
|
+
* Other:
|
|
15
|
+
|
|
16
|
+
* Use MuPDF-1.25.6.
|
|
17
|
+
* Fixed MuPDF SEGV on MacOS with particular fonts.
|
|
18
|
+
* Fixed `Annot.get_textpage()`'s `clip` arg.
|
|
19
|
+
* Fixed Python-3.14 (pre-release) build error.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
**Changes in version 1.25.4 (2025-03-14)**
|
|
23
|
+
|
|
24
|
+
* Use MuPDF-1.25.5.
|
|
25
|
+
|
|
26
|
+
* Fixed issues:
|
|
27
|
+
|
|
28
|
+
* **Fixed** `4079 <https://github.com/pymupdf/PyMuPDF/issues/4079>`_: Unexpected result for apply_redactions()
|
|
29
|
+
* **Fixed** `4224 <https://github.com/pymupdf/PyMuPDF/issues/4224>`_: MuPDF error: format error: negative code in 1d faxd
|
|
30
|
+
* **Fixed** `4303 <https://github.com/pymupdf/PyMuPDF/issues/4303>`_: page.get_image_info() returns outdated cached results after replacing image
|
|
31
|
+
* **Fixed** `4309 <https://github.com/pymupdf/PyMuPDF/issues/4309>`_: FzErrorFormat Error When Deleting First Page
|
|
32
|
+
* **Fixed** `4336 <https://github.com/pymupdf/PyMuPDF/issues/4336>`_: Major Performance Regression: pix.color_count is 150x slower in version 1.25.3 compared to 1.23.8
|
|
33
|
+
* **Fixed** `4341 <https://github.com/pymupdf/PyMuPDF/issues/4341>`_: Invalid label retrieval when /Kids is an array of multiple /Nums
|
|
34
|
+
|
|
35
|
+
* Other:
|
|
36
|
+
|
|
37
|
+
* Fixed handling of duplicate widget names when joining PDFs (PR #4347).
|
|
38
|
+
* Improved Pyodide build.
|
|
39
|
+
* Avoid SWIG-related build errors with Python-3.13 by disabling PY_LIMITED_API.
|
|
40
|
+
|
|
41
|
+
|
|
5
42
|
**Changes in version 1.25.3 (2025-02-06)**
|
|
6
43
|
|
|
7
44
|
* Use MuPDF-1.25.4.
|
|
@@ -9,7 +46,7 @@ Change Log
|
|
|
9
46
|
* Fixed issues:
|
|
10
47
|
|
|
11
48
|
* **Fixed** `4139 <https://github.com/pymupdf/PyMuPDF/issues/4139>`_: Text color numbers change between 1.24.14 and 1.25.0
|
|
12
|
-
* **Fixed** `4141 <https://github.com/pymupdf/PyMuPDF/issues/4141>`_: Some insertion methods fails for pages without a /
|
|
49
|
+
* **Fixed** `4141 <https://github.com/pymupdf/PyMuPDF/issues/4141>`_: Some insertion methods fails for pages without a /Resources object
|
|
13
50
|
* **Fixed** `4180 <https://github.com/pymupdf/PyMuPDF/issues/4180>`_: Search problems
|
|
14
51
|
* **Fixed** `4182 <https://github.com/pymupdf/PyMuPDF/issues/4182>`_: Text coordinate extraction error
|
|
15
52
|
* **Fixed** `4245 <https://github.com/pymupdf/PyMuPDF/issues/4245>`_: Highlighting issue distorted on recent versions
|
|
@@ -20,10 +57,11 @@ Change Log
|
|
|
20
57
|
* In annotations:
|
|
21
58
|
* Added support for subtype FreeTextCallout.
|
|
22
59
|
* Added support for rich text.
|
|
23
|
-
* Added miter_limit arg to insert_text*() to allow
|
|
60
|
+
* Added miter_limit arg to insert_text*() to allow suppression of spikes caused by long miters.
|
|
24
61
|
* Add Widget Support to `Document.insert_pdf()`.
|
|
25
62
|
* Add `bibi` to span dicts.
|
|
26
63
|
* Add `synthetic' to char dict.
|
|
64
|
+
* Fixed Pyodide builds.
|
|
27
65
|
|
|
28
66
|
|
|
29
67
|
**Changes in version 1.25.2 (2025-01-17)**
|
|
@@ -65,9 +103,13 @@ Change Log
|
|
|
65
103
|
|
|
66
104
|
* **Fixed** `4026 <https://github.com/pymupdf/PyMuPDF/issues/4026>`_: page.get_text('blocks') output two piece of very similar text with different bbox
|
|
67
105
|
* **Fixed** `4004 <https://github.com/pymupdf/PyMuPDF/issues/4004>`_: Segmentation Fault When Updating PDF Form Field Value
|
|
106
|
+
* **Fixed** `3887 <https://github.com/pymupdf/PyMuPDF/issues/3887>`_: Subset Fonts problem using Fallback Font
|
|
107
|
+
* **Fixed** `3886 <https://github.com/pymupdf/PyMuPDF/issues/3886>`_: Another issue with destroying PDF when inserting html
|
|
68
108
|
* **Fixed** `3751 <https://github.com/pymupdf/PyMuPDF/issues/3751>`_: apply_redactions causes part of the page content to be hidden / transparent
|
|
69
109
|
|
|
70
110
|
|
|
111
|
+
.. codespell:ignore-begin
|
|
112
|
+
|
|
71
113
|
**Changes in version 1.24.14 (2024-11-19)**
|
|
72
114
|
|
|
73
115
|
* Use MuPDF-1.24.11.
|
|
@@ -2624,3 +2666,5 @@ Changes in version 1.9.1 compared to version 1.8.0 are the following:
|
|
|
2624
2666
|
* Incremental saves for changes are possible now using the call pattern *doc.save(doc.name, incremental=True)*.
|
|
2625
2667
|
* A PDF's metadata can now be deleted, set or changed by document method *set_metadata()*. Supports incremental saves.
|
|
2626
2668
|
* A PDF's bookmarks (or table of contents) can now be deleted, set or changed with the entries of a list using document method *set_toc(list)*. Supports incremental saves.
|
|
2669
|
+
|
|
2670
|
+
.. codespell:ignore-end
|
|
@@ -118,6 +118,8 @@ def main():
|
|
|
118
118
|
log(f'{sys.executable=}')
|
|
119
119
|
log(f'{platform.python_version()=}')
|
|
120
120
|
log(f'{__file__=}')
|
|
121
|
+
log(f'{os.environ.get("PYMUDF_SCRIPTS_SYSINSTALL_ARGS_PRE")=}')
|
|
122
|
+
log(f'{os.environ.get("PYMUDF_SCRIPTS_SYSINSTALL_ARGS_POST")=}')
|
|
121
123
|
log(f'{sys.argv=}')
|
|
122
124
|
log(f'{sysconfig.get_path("platlib")=}')
|
|
123
125
|
run_command(f'python -V', check=0)
|
|
@@ -152,7 +154,9 @@ def main():
|
|
|
152
154
|
|
|
153
155
|
# Parse command-line.
|
|
154
156
|
#
|
|
155
|
-
|
|
157
|
+
env_args_pre = shlex.split(os.environ.get('PYMUDF_SCRIPTS_SYSINSTALL_ARGS_PRE', ''))
|
|
158
|
+
env_args_post = shlex.split(os.environ.get('PYMUDF_SCRIPTS_SYSINSTALL_ARGS_POST', ''))
|
|
159
|
+
args = iter(env_args_pre + sys.argv[1:] + env_args_post)
|
|
156
160
|
while 1:
|
|
157
161
|
try:
|
|
158
162
|
arg = next(args)
|
|
@@ -240,6 +244,9 @@ def main():
|
|
|
240
244
|
command += f' HAVE_LEPTONICA=yes'
|
|
241
245
|
command += f' HAVE_TESSERACT=yes'
|
|
242
246
|
command += f' USE_SYSTEM_LIBS=yes'
|
|
247
|
+
# We need latest zxingcpp so system version not ok.
|
|
248
|
+
command += f' USE_SYSTEM_ZXINGCPP=no'
|
|
249
|
+
command += f' barcode=yes'
|
|
243
250
|
command += f' VENV_FLAG={"--venv" if pip == "venv" else ""}'
|
|
244
251
|
if mupdf_so_mode:
|
|
245
252
|
command += f' SO_INSTALL_MODE={mupdf_so_mode}'
|
|
@@ -291,7 +298,7 @@ def main():
|
|
|
291
298
|
run(f'{sudo}rm -r {p}/site-packages/pymupdf.py || true')
|
|
292
299
|
run(f'{sudo}rm -r {p}/site-packages/fitz || true')
|
|
293
300
|
run(f'{sudo}rm -r {p}/site-packages/fitz.py || true')
|
|
294
|
-
run(f'{sudo}rm -r {p}/site-packages/
|
|
301
|
+
run(f'{sudo}rm -r {p}/site-packages/pymupdf-*.dist-info || true')
|
|
295
302
|
run(f'{sudo}rm -r {root_prefix}/bin/pymupdf || true')
|
|
296
303
|
if pip == 'venv':
|
|
297
304
|
run(f'{sudo}{venv_name}/bin/python -m installer --destdir {root} --prefix {prefix} {wheel}')
|
|
@@ -94,10 +94,16 @@ Options:
|
|
|
94
94
|
Whether to rebuild mupdf when we build PyMuPDF. Default is 1.
|
|
95
95
|
--gdb 0|1
|
|
96
96
|
Run tests under gdb. Requires user interaction.
|
|
97
|
+
--pytest-prefix <command>
|
|
98
|
+
Use specified prefix when running pytest. E.g. `gdb --args`.
|
|
97
99
|
--pybind 0|1
|
|
98
100
|
Experimental, for investigating
|
|
99
101
|
https://github.com/pymupdf/PyMuPDF/issues/3869. Runs run basic code
|
|
100
102
|
inside C++ pybind. Requires `sudo apt install pybind11-dev` or similar.
|
|
103
|
+
--pymupdf-pypi <name>
|
|
104
|
+
Do not build PyMuPDF, instead install with `pip install <name>`. For
|
|
105
|
+
example allows testing of a specific version with `--pymupdf-pypi
|
|
106
|
+
pymupdf==x.y.z`.
|
|
101
107
|
--system-site-packages 0|1
|
|
102
108
|
If 1, use `--system-site-packages` when creating venv. Defaults is 0.
|
|
103
109
|
--timeout <seconds>
|
|
@@ -123,6 +129,11 @@ Commands:
|
|
|
123
129
|
`pyodide build`. This runs our setup.py with CC etc set up
|
|
124
130
|
to create Pyodide binaries in a wheel called, for example,
|
|
125
131
|
`PyMuPDF-1.23.2-cp311-none-emscripten_3_1_32_wasm32.whl`.
|
|
132
|
+
|
|
133
|
+
It seems that sys.version must match the Python version inside emsdk;
|
|
134
|
+
as of 2025-02-14 this is 3.12. Otherwise we get build errors such as:
|
|
135
|
+
[wasm-validator error in function 723] unexpected false: all used features should be allowed, on ...
|
|
136
|
+
|
|
126
137
|
|
|
127
138
|
Environment:
|
|
128
139
|
PYMUDF_SCRIPTS_TEST_options
|
|
@@ -159,6 +170,9 @@ def main(argv):
|
|
|
159
170
|
if len(argv) == 1:
|
|
160
171
|
show_help()
|
|
161
172
|
return
|
|
173
|
+
|
|
174
|
+
log(f'{sys.executable=}')
|
|
175
|
+
log(f'{sys.version=}')
|
|
162
176
|
|
|
163
177
|
build_isolation = None
|
|
164
178
|
valgrind = False
|
|
@@ -173,6 +187,7 @@ def main(argv):
|
|
|
173
187
|
implementations = 'r'
|
|
174
188
|
test_names = list()
|
|
175
189
|
venv = 2
|
|
190
|
+
pytest_prefix = None
|
|
176
191
|
pybind = False
|
|
177
192
|
pytest_options = None
|
|
178
193
|
timeout = None
|
|
@@ -180,6 +195,7 @@ def main(argv):
|
|
|
180
195
|
system_site_packages = False
|
|
181
196
|
pyodide_build_version = None
|
|
182
197
|
packages = False
|
|
198
|
+
pymupdf_pypi = None
|
|
183
199
|
|
|
184
200
|
options = os.environ.get('PYMUDF_SCRIPTS_TEST_options', '')
|
|
185
201
|
options = shlex.split(options)
|
|
@@ -209,7 +225,7 @@ def main(argv):
|
|
|
209
225
|
implementations = next(args)
|
|
210
226
|
elif arg in ('--mupdf', '-m'):
|
|
211
227
|
mupdf = next(args)
|
|
212
|
-
if not mupdf.startswith('git:') and mupdf != '-':
|
|
228
|
+
if not mupdf.startswith('git:') and '://' not in mupdf and mupdf != '-':
|
|
213
229
|
assert os.path.isdir(mupdf), f'Not a directory: {mupdf=}.'
|
|
214
230
|
mupdf = os.path.abspath(mupdf)
|
|
215
231
|
os.environ['PYMUPDF_SETUP_MUPDF_BUILD'] = mupdf
|
|
@@ -223,6 +239,8 @@ def main(argv):
|
|
|
223
239
|
value = next(args)
|
|
224
240
|
assert value in ('0', '1'), f'`-s` must be followed by `0` or `1`, not {value=}.'
|
|
225
241
|
os.environ['PYMUPDF_SETUP_PY_LIMITED_API'] = value
|
|
242
|
+
elif arg == '--pytest-prefix':
|
|
243
|
+
pytest_prefix = next(args)
|
|
226
244
|
elif arg == '--pybind':
|
|
227
245
|
pybind = int(next(args))
|
|
228
246
|
elif arg == '--system-site-packages':
|
|
@@ -245,6 +263,8 @@ def main(argv):
|
|
|
245
263
|
valgrind_args = next(args)
|
|
246
264
|
elif arg == '--pyodide-build-version':
|
|
247
265
|
pyodide_build_version = next(args)
|
|
266
|
+
elif arg == '--pymupdf-pypi':
|
|
267
|
+
pymupdf_pypi = next(args)
|
|
248
268
|
else:
|
|
249
269
|
assert 0, f'Unrecognised option: {arg=}.'
|
|
250
270
|
|
|
@@ -276,14 +296,17 @@ def main(argv):
|
|
|
276
296
|
return
|
|
277
297
|
|
|
278
298
|
def do_build(wheel=False):
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
299
|
+
if pymupdf_pypi:
|
|
300
|
+
run(f'pip install --force-reinstall {pymupdf_pypi}')
|
|
301
|
+
else:
|
|
302
|
+
build(
|
|
303
|
+
build_type=build_type,
|
|
304
|
+
build_isolation=build_isolation,
|
|
305
|
+
venv_quick=venv_quick,
|
|
306
|
+
build_mupdf=build_mupdf,
|
|
307
|
+
build_flavour=build_flavour,
|
|
308
|
+
wheel=wheel,
|
|
309
|
+
)
|
|
287
310
|
def do_test():
|
|
288
311
|
test(
|
|
289
312
|
implementations=implementations,
|
|
@@ -294,6 +317,7 @@ def main(argv):
|
|
|
294
317
|
pytest_options=pytest_options,
|
|
295
318
|
timeout=timeout,
|
|
296
319
|
gdb=gdb,
|
|
320
|
+
pytest_prefix=pytest_prefix,
|
|
297
321
|
test_fitz=test_fitz,
|
|
298
322
|
pytest_k=pytest_k,
|
|
299
323
|
pybind=pybind,
|
|
@@ -474,7 +498,10 @@ def build_pyodide_wheel(pyodide_build_version=None):
|
|
|
474
498
|
#
|
|
475
499
|
env_extra['PYMUPDF_SETUP_MUPDF_TESSERACT'] = '0'
|
|
476
500
|
setup = pyodide_setup(pymupdf_dir, pyodide_build_version=pyodide_build_version)
|
|
477
|
-
command = f'{setup} && pyodide build --exports
|
|
501
|
+
command = f'{setup} && echo "### Running pyodide build" && pyodide build --exports whole_archive'
|
|
502
|
+
|
|
503
|
+
command = command.replace(' && ', '\n && ')
|
|
504
|
+
|
|
478
505
|
run(command, env_extra=env_extra)
|
|
479
506
|
|
|
480
507
|
# Copy wheel into `wheelhouse/` so it is picked up as a workflow
|
|
@@ -499,23 +526,35 @@ def pyodide_setup(
|
|
|
499
526
|
clean:
|
|
500
527
|
If true we create an entirely new environment. Otherwise
|
|
501
528
|
we reuse any existing emsdk repository and venv.
|
|
529
|
+
pyodide_build_version:
|
|
530
|
+
Version of Python package pyodide-build; if None we use latest
|
|
531
|
+
available version.
|
|
532
|
+
2025-02-13: pyodide_build_version='0.29.3' works.
|
|
533
|
+
|
|
534
|
+
The returned command does the following:
|
|
502
535
|
|
|
503
|
-
*
|
|
504
|
-
|
|
536
|
+
* Checkout latest emsdk from https://github.com/emscripten-core/emsdk.git:
|
|
537
|
+
* Clone emsdk repository to `emsdk` if not already present.
|
|
538
|
+
* Run `git pull -r` inside emsdk checkout.
|
|
539
|
+
* Create venv `venv_pyodide_<python_version>` if not already present.
|
|
540
|
+
* Activate venv `venv_pyodide_<python_version>`.
|
|
505
541
|
* Install/upgrade package `pyodide-build`.
|
|
506
542
|
* Run emsdk install scripts and enter emsdk environment.
|
|
507
|
-
* Replace emsdk/upstream/bin/wasm-opt
|
|
508
|
-
(https://github.com/pyodide/pyodide/issues/4048).
|
|
509
543
|
|
|
510
544
|
Example usage in a build function:
|
|
511
545
|
|
|
512
|
-
command =
|
|
546
|
+
command = pyodide_setup()
|
|
513
547
|
command += ' && pyodide build --exports pyinit'
|
|
514
548
|
subprocess.run(command, shell=1, check=1)
|
|
515
549
|
'''
|
|
550
|
+
|
|
551
|
+
pv = platform.python_version_tuple()[:2]
|
|
552
|
+
assert pv == ('3', '12'), f'Pyodide builds need to be run with Python-3.12 but current Python is {platform.python_version()}.'
|
|
516
553
|
command = f'cd {directory}'
|
|
517
554
|
|
|
518
|
-
# Clone emsdk.
|
|
555
|
+
# Clone/update emsdk. We always use the latest emsdk with `git pull`.
|
|
556
|
+
#
|
|
557
|
+
# 2025-02-13: this works: 2514ec738de72cebbba7f4fdba0cf2fabcb779a5
|
|
519
558
|
#
|
|
520
559
|
dir_emsdk = 'emsdk'
|
|
521
560
|
if clean:
|
|
@@ -524,106 +563,40 @@ def pyodide_setup(
|
|
|
524
563
|
# important to remove it here.
|
|
525
564
|
shutil.rmtree('.pyodide-xbuildenv', ignore_errors=1)
|
|
526
565
|
if not os.path.exists(f'{directory}/{dir_emsdk}'):
|
|
527
|
-
command += f' && echo "###
|
|
566
|
+
command += f' && echo "### Cloning emsdk.git"'
|
|
528
567
|
command += f' && git clone https://github.com/emscripten-core/emsdk.git {dir_emsdk}'
|
|
568
|
+
command += f' && echo "### Updating checkout {dir_emsdk}"'
|
|
569
|
+
command += f' && (cd {dir_emsdk} && git pull -r)'
|
|
570
|
+
command += f' && echo "### Checkout {dir_emsdk} is:"'
|
|
571
|
+
command += f' && (cd {dir_emsdk} && git show -s --oneline)'
|
|
529
572
|
|
|
530
573
|
# Create and enter Python venv.
|
|
531
574
|
#
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
python = sys.executable
|
|
536
|
-
a, b = sys.version_info[:2]
|
|
537
|
-
venv_pyodide = f'venv_pyodide_{a}.{b}'
|
|
538
|
-
else:
|
|
539
|
-
pyodide_build_version = '0.23.4'
|
|
540
|
-
venv_pyodide = 'venv_pyodide_3.11'
|
|
541
|
-
python = sys.executable
|
|
542
|
-
if sys.version_info[:2] != (3, 11):
|
|
543
|
-
log(f'Forcing use of python-3.11 because {sys.version=} is not 3.11.')
|
|
544
|
-
python = 'python3.11'
|
|
575
|
+
python = sys.executable
|
|
576
|
+
venv_pyodide = f'venv_pyodide_{sys.version_info[0]}.{sys.version_info[1]}'
|
|
577
|
+
|
|
545
578
|
if not os.path.exists( f'{directory}/{venv_pyodide}'):
|
|
546
|
-
command += f' && echo "###
|
|
579
|
+
command += f' && echo "### Creating venv {venv_pyodide}"'
|
|
547
580
|
command += f' && {python} -m venv {venv_pyodide}'
|
|
548
581
|
command += f' && . {venv_pyodide}/bin/activate'
|
|
549
|
-
command += f' && echo "###
|
|
550
|
-
command += f' && python -m pip install --upgrade pip wheel pyodide-build
|
|
551
|
-
|
|
582
|
+
command += f' && echo "### Installing Python packages."'
|
|
583
|
+
command += f' && python -m pip install --upgrade pip wheel pyodide-build'
|
|
584
|
+
if pyodide_build_version:
|
|
585
|
+
command += f'=={pyodide_build_version}'
|
|
552
586
|
|
|
553
587
|
# Run emsdk install scripts and enter emsdk environment.
|
|
554
588
|
#
|
|
555
589
|
command += f' && cd {dir_emsdk}'
|
|
556
590
|
command += ' && PYODIDE_EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)'
|
|
557
|
-
command += ' && echo "###
|
|
591
|
+
command += ' && echo "### PYODIDE_EMSCRIPTEN_VERSION is: $PYODIDE_EMSCRIPTEN_VERSION"'
|
|
592
|
+
command += ' && echo "### Running ./emsdk install"'
|
|
558
593
|
command += ' && ./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}'
|
|
559
|
-
command += ' && echo "###
|
|
594
|
+
command += ' && echo "### Running ./emsdk activate"'
|
|
560
595
|
command += ' && ./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}'
|
|
561
|
-
command += ' && echo "###
|
|
596
|
+
command += ' && echo "### Running ./emsdk_env.sh"'
|
|
562
597
|
command += ' && . ./emsdk_env.sh' # Need leading `./` otherwise weird 'Not found' error.
|
|
563
598
|
|
|
564
|
-
if pyodide_build_version:
|
|
565
|
-
command += ' && echo "### Not patching emsdk"'
|
|
566
|
-
else:
|
|
567
|
-
# Make our returned command replace emsdk/upstream/bin/wasm-opt
|
|
568
|
-
# with a script that does nothing, otherwise the linker
|
|
569
|
-
# command fails after it has created the output file. See:
|
|
570
|
-
# https://github.com/pyodide/pyodide/issues/4048
|
|
571
|
-
#
|
|
572
|
-
|
|
573
|
-
def write( text, path):
|
|
574
|
-
with open( path, 'w') as f:
|
|
575
|
-
f.write( text)
|
|
576
|
-
os.chmod( path, 0o755)
|
|
577
|
-
|
|
578
|
-
# Create a script that our command runs, that overwrites
|
|
579
|
-
# `emsdk/upstream/bin/wasm-opt`, hopefully in a way that is
|
|
580
|
-
# idempotent.
|
|
581
|
-
#
|
|
582
|
-
# The script moves the original wasm-opt to wasm-opt-0.
|
|
583
|
-
#
|
|
584
|
-
write(
|
|
585
|
-
textwrap.dedent('''
|
|
586
|
-
#! /usr/bin/env python3
|
|
587
|
-
import os
|
|
588
|
-
p = 'upstream/bin/wasm-opt'
|
|
589
|
-
p0 = 'upstream/bin/wasm-opt-0'
|
|
590
|
-
p1 = '../wasm-opt-1'
|
|
591
|
-
if os.path.exists( p0):
|
|
592
|
-
print(f'### {__file__}: {p0!r} already exists so not overwriting from {p!r}.')
|
|
593
|
-
else:
|
|
594
|
-
s = os.stat( p)
|
|
595
|
-
assert s.st_size > 15000000, f'File smaller ({s.st_size}) than expected: {p!r}'
|
|
596
|
-
print(f'### {__file__}: Moving {p!r} -> {p0!r}.')
|
|
597
|
-
os.rename( p, p0)
|
|
598
|
-
print(f'### {__file__}: Moving {p1!r} -> {p!r}.')
|
|
599
|
-
os.rename( p1, p)
|
|
600
|
-
'''
|
|
601
|
-
).strip(),
|
|
602
|
-
f'{directory}/wasm-opt-replace.py',
|
|
603
|
-
)
|
|
604
|
-
|
|
605
|
-
# Create a wasm-opt script that basically does nothing, except
|
|
606
|
-
# defers to the original script when run with `--version`.
|
|
607
|
-
#
|
|
608
|
-
write(
|
|
609
|
-
textwrap.dedent('''
|
|
610
|
-
#!/usr/bin/env python3
|
|
611
|
-
import os
|
|
612
|
-
import sys
|
|
613
|
-
import subprocess
|
|
614
|
-
if sys.argv[1:] == ['--version']:
|
|
615
|
-
root = os.path.dirname(__file__)
|
|
616
|
-
subprocess.run(f'{root}/wasm-opt-0 --version', shell=1, check=1)
|
|
617
|
-
else:
|
|
618
|
-
print(f'{__file__}: Doing nothing. {sys.argv=}')
|
|
619
|
-
'''
|
|
620
|
-
).strip(),
|
|
621
|
-
f'{directory}/wasm-opt-1',
|
|
622
|
-
)
|
|
623
|
-
command += ' && ../wasm-opt-replace.py'
|
|
624
|
-
|
|
625
599
|
command += ' && cd ..'
|
|
626
|
-
|
|
627
600
|
return command
|
|
628
601
|
|
|
629
602
|
|
|
@@ -636,6 +609,7 @@ def test(
|
|
|
636
609
|
pytest_options=None,
|
|
637
610
|
timeout=None,
|
|
638
611
|
gdb=False,
|
|
612
|
+
pytest_prefix=None,
|
|
639
613
|
test_fitz=True,
|
|
640
614
|
pytest_k=None,
|
|
641
615
|
pybind=False,
|
|
@@ -657,6 +631,8 @@ def test(
|
|
|
657
631
|
See top-level option `-p`.
|
|
658
632
|
gdb:
|
|
659
633
|
See top-level option `--gdb`.
|
|
634
|
+
pytest_prefix:
|
|
635
|
+
See top-level option `--pytest-prefix`.
|
|
660
636
|
test_fitz:
|
|
661
637
|
See top-level option `-f`.
|
|
662
638
|
'''
|
|
@@ -765,6 +741,8 @@ def test(
|
|
|
765
741
|
)
|
|
766
742
|
elif gdb:
|
|
767
743
|
command = f'{python} {pymupdf_dir_rel}/tests/run_compound.py{run_compound_args} gdb --args {python} -m pytest {pytest_options} {pytest_arg}'
|
|
744
|
+
elif pytest_prefix:
|
|
745
|
+
command = f'{python} {pymupdf_dir_rel}/tests/run_compound.py{run_compound_args} {pytest_prefix} {python} -m pytest {pytest_options} {pytest_arg}'
|
|
768
746
|
elif platform.system() == 'Windows':
|
|
769
747
|
# `python -m pytest` doesn't seem to work.
|
|
770
748
|
command = f'{python} {pymupdf_dir_rel}/tests/run_compound.py{run_compound_args} pytest {pytest_options} {pytest_arg}'
|
|
@@ -152,6 +152,11 @@ Environmental variables:
|
|
|
152
152
|
|
|
153
153
|
PYMUPDF_SETUP_PY_LIMITED_API
|
|
154
154
|
If not '0', we build for current Python's stable ABI.
|
|
155
|
+
|
|
156
|
+
However if unset and we are on Python-3.13 or later, we do
|
|
157
|
+
not build for the stable ABI because as of 2025-03-04 SWIG
|
|
158
|
+
generates incorrect stable ABI code with Python-3.13 - see:
|
|
159
|
+
https://github.com/swig/swig/issues/3059
|
|
155
160
|
|
|
156
161
|
PYMUPDF_SETUP_URL_WHEEL
|
|
157
162
|
If set, we use an existing wheel instead of building a new wheel.
|
|
@@ -208,6 +213,11 @@ def log( text):
|
|
|
208
213
|
sys.stdout.flush()
|
|
209
214
|
|
|
210
215
|
|
|
216
|
+
def run(command, check=1):
|
|
217
|
+
log(f'Running: {command}')
|
|
218
|
+
return subprocess.run( command, shell=1, check=check)
|
|
219
|
+
|
|
220
|
+
|
|
211
221
|
if 1:
|
|
212
222
|
# For debugging.
|
|
213
223
|
log(f'### Starting.')
|
|
@@ -218,6 +228,7 @@ if 1:
|
|
|
218
228
|
log(f'CPU bits: {32 if sys.maxsize == 2**31 - 1 else 64} {sys.maxsize=}')
|
|
219
229
|
log(f'__file__: {__file__!r}')
|
|
220
230
|
log(f'os.getcwd(): {os.getcwd()!r}')
|
|
231
|
+
log(f'getconf ARG_MAX: {pipcl.run("getconf ARG_MAX", capture=1, check=0, verbose=0)!r}')
|
|
221
232
|
log(f'sys.argv ({len(sys.argv)}):')
|
|
222
233
|
for i, arg in enumerate(sys.argv):
|
|
223
234
|
log(f' {i}: {arg!r}')
|
|
@@ -236,10 +247,16 @@ g_root = os.path.abspath( f'{__file__}/..')
|
|
|
236
247
|
# Name of file that identifies that we are in a PyMuPDF sdist.
|
|
237
248
|
g_pymupdfb_sdist_marker = 'pymupdfb_sdist'
|
|
238
249
|
|
|
250
|
+
python_version_tuple = tuple(int(x) for x in platform.python_version_tuple()[:2])
|
|
251
|
+
|
|
239
252
|
PYMUPDF_SETUP_PY_LIMITED_API = os.environ.get('PYMUPDF_SETUP_PY_LIMITED_API')
|
|
240
253
|
assert PYMUPDF_SETUP_PY_LIMITED_API in (None, '', '0', '1'), \
|
|
241
254
|
f'Should be "", "0", "1" or undefined: {PYMUPDF_SETUP_PY_LIMITED_API=}.'
|
|
242
|
-
|
|
255
|
+
if PYMUPDF_SETUP_PY_LIMITED_API is None and python_version_tuple >= (3, 13):
|
|
256
|
+
log(f'Not defaulting to Python limited api because {platform.python_version_tuple()=}.')
|
|
257
|
+
g_py_limited_api = False
|
|
258
|
+
else:
|
|
259
|
+
g_py_limited_api = (PYMUPDF_SETUP_PY_LIMITED_API != '0')
|
|
243
260
|
|
|
244
261
|
PYMUPDF_SETUP_URL_WHEEL = os.environ.get('PYMUPDF_SETUP_URL_WHEEL')
|
|
245
262
|
log(f'{PYMUPDF_SETUP_URL_WHEEL=}')
|
|
@@ -271,11 +288,6 @@ def _fs_remove(path):
|
|
|
271
288
|
assert not os.path.exists( path)
|
|
272
289
|
|
|
273
290
|
|
|
274
|
-
def run(command, check=1):
|
|
275
|
-
log(f'Running: {command}')
|
|
276
|
-
return subprocess.run( command, shell=1, check=check)
|
|
277
|
-
|
|
278
|
-
|
|
279
291
|
def _git_get_branch( directory):
|
|
280
292
|
command = f'cd {directory} && git branch --show-current'
|
|
281
293
|
log( f'Running: {command}')
|
|
@@ -364,7 +376,7 @@ def tar_extract(path, mode='r:gz', prefix=None, exists='raise'):
|
|
|
364
376
|
return prefix_actual
|
|
365
377
|
|
|
366
378
|
|
|
367
|
-
def
|
|
379
|
+
def git_info( directory):
|
|
368
380
|
'''
|
|
369
381
|
Returns `(sha, comment, diff, branch)`, all items are str or None if not
|
|
370
382
|
available.
|
|
@@ -390,10 +402,42 @@ def get_git_id( directory):
|
|
|
390
402
|
)
|
|
391
403
|
if cp.returncode == 0:
|
|
392
404
|
branch = cp.stdout.strip()
|
|
393
|
-
log(f'
|
|
405
|
+
log(f'git_info(): directory={directory!r} returning branch={branch!r} sha={sha!r} comment={comment!r}')
|
|
394
406
|
return sha, comment, diff, branch
|
|
395
407
|
|
|
396
408
|
|
|
409
|
+
def git_patch(directory, patch, hard=False):
|
|
410
|
+
'''
|
|
411
|
+
Applies string <patch> with `git patch` in <directory>.
|
|
412
|
+
|
|
413
|
+
If <hard> is true we clean the tree with `git checkout .` and then apply
|
|
414
|
+
the patch.
|
|
415
|
+
|
|
416
|
+
Otherwise we apply patch only if it is not already applied; this might fail
|
|
417
|
+
if there are conflicting changes in the tree.
|
|
418
|
+
'''
|
|
419
|
+
log(f'Applying patch in {directory}:\n{textwrap.indent(patch, " ")}')
|
|
420
|
+
if not patch:
|
|
421
|
+
return
|
|
422
|
+
# Carriage returns break `git apply` so we use `newline='\n'` in open().
|
|
423
|
+
path = os.path.abspath(f'{directory}/pymupdf_patch.txt')
|
|
424
|
+
with open(path, 'w', newline='\n') as f:
|
|
425
|
+
f.write(patch)
|
|
426
|
+
log(f'Using patch file: {path}')
|
|
427
|
+
if hard:
|
|
428
|
+
run(f'cd {directory} && git checkout .')
|
|
429
|
+
run(f'cd {directory} && git apply {path}')
|
|
430
|
+
log(f'Have applied patch in {directory}.')
|
|
431
|
+
else:
|
|
432
|
+
e = run( f'cd {directory} && git apply --check --reverse {path}', check=0)
|
|
433
|
+
if e == 0:
|
|
434
|
+
log(f'Not patching {directory} because already patched.')
|
|
435
|
+
else:
|
|
436
|
+
run(f'cd {directory} && git apply {path}')
|
|
437
|
+
log(f'Have applied patch in {directory}.')
|
|
438
|
+
run(f'cd {directory} && git diff')
|
|
439
|
+
|
|
440
|
+
|
|
397
441
|
mupdf_tgz = os.path.abspath( f'{__file__}/../mupdf.tgz')
|
|
398
442
|
|
|
399
443
|
def get_mupdf_internal(out, location=None, sha=None, local_tgz=None):
|
|
@@ -444,7 +488,8 @@ def get_mupdf_internal(out, location=None, sha=None, local_tgz=None):
|
|
|
444
488
|
if e:
|
|
445
489
|
# No existing git checkout, so do a fresh clone.
|
|
446
490
|
_fs_remove(local_dir)
|
|
447
|
-
|
|
491
|
+
gitargs = location[4:]
|
|
492
|
+
run(f'git clone --recursive --depth 1 --shallow-submodules {gitargs} {local_dir}')
|
|
448
493
|
|
|
449
494
|
# Show sha of checkout.
|
|
450
495
|
run( f'cd {local_dir} && git show --pretty=oneline|head -n 1', check=False)
|
|
@@ -624,6 +669,7 @@ def build():
|
|
|
624
669
|
add('p', f'{g_root}/src/pymupdf.py', to_dir)
|
|
625
670
|
add('p', f'{g_root}/src/table.py', to_dir)
|
|
626
671
|
add('p', f'{g_root}/src/utils.py', to_dir)
|
|
672
|
+
add('p', f'{g_root}/src/_wxcolors.py', to_dir)
|
|
627
673
|
add('p', f'{g_root}/src/_apply_pages.py', to_dir)
|
|
628
674
|
add('p', f'{g_root}/src/build/extra.py', to_dir)
|
|
629
675
|
if path_so_leaf:
|
|
@@ -856,6 +902,34 @@ def build_mupdf_unix(
|
|
|
856
902
|
|
|
857
903
|
if openbsd or freebsd:
|
|
858
904
|
env_add(env, 'CXX', 'c++', ' ')
|
|
905
|
+
|
|
906
|
+
if darwin and os.environ.get('GITHUB_ACTIONS') == 'true':
|
|
907
|
+
if os.environ.get('ImageOS') == 'macos13':
|
|
908
|
+
# On Github macos13 we need to use Clang/LLVM (Homebrew) 15.0.7,
|
|
909
|
+
# otherwise mupdf:thirdparty/tesseract/src/api/baseapi.cpp fails to
|
|
910
|
+
# compile with:
|
|
911
|
+
#
|
|
912
|
+
# thirdparty/tesseract/src/api/baseapi.cpp:150:25: error: 'recursive_directory_iterator' is unavailable: introduced in macOS 10.15
|
|
913
|
+
#
|
|
914
|
+
# See:
|
|
915
|
+
# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md
|
|
916
|
+
#
|
|
917
|
+
log(f'Using llvm@15 clang and clang++')
|
|
918
|
+
cl15 = pipcl.run(f'brew --prefix llvm@15', capture=1)
|
|
919
|
+
log(f'{cl15=}')
|
|
920
|
+
cl15 = cl15.strip()
|
|
921
|
+
pipcl.run(f'ls -lL {cl15}')
|
|
922
|
+
pipcl.run(f'ls -lL {cl15}/bin')
|
|
923
|
+
cc = f'{cl15}/bin/clang'
|
|
924
|
+
cxx = f'{cl15}/bin/clang++'
|
|
925
|
+
env['CC'] = cc
|
|
926
|
+
env['CXX'] = cxx
|
|
927
|
+
|
|
928
|
+
# Show compiler versions.
|
|
929
|
+
cc = env.get('CC', 'cc')
|
|
930
|
+
cxx = env.get('CXX', 'c++')
|
|
931
|
+
pipcl.run(f'{cc} --version')
|
|
932
|
+
pipcl.run(f'{cxx} --version')
|
|
859
933
|
|
|
860
934
|
# Add extra flags for MacOS cross-compilation, where ARCHFLAGS can be
|
|
861
935
|
# '-arch arm64'.
|
|
@@ -865,6 +939,8 @@ def build_mupdf_unix(
|
|
|
865
939
|
env_add(env, 'XCFLAGS', archflags)
|
|
866
940
|
env_add(env, 'XLIBS', archflags)
|
|
867
941
|
|
|
942
|
+
mupdf_version_tuple = get_mupdf_version(mupdf_local)
|
|
943
|
+
|
|
868
944
|
# We specify a build directory path containing 'pymupdf' so that we
|
|
869
945
|
# coexist with non-PyMuPDF builds (because PyMuPDF builds have a
|
|
870
946
|
# different config.h).
|
|
@@ -877,7 +953,16 @@ def build_mupdf_unix(
|
|
|
877
953
|
# $_PYTHON_HOST_PLATFORM allows cross-compiled cibuildwheel builds
|
|
878
954
|
# to coexist, e.g. on github.
|
|
879
955
|
#
|
|
956
|
+
# Have experimented with looking at getconf_ARG_MAX to decide whether to
|
|
957
|
+
# omit `PyMuPDF-` from the build directory, to avoid command-too-long
|
|
958
|
+
# errors with mupdf-1.26. But it seems that `getconf ARG_MAX` returns
|
|
959
|
+
# a system limit, not the actual limit of the current shell, and there
|
|
960
|
+
# doesn't seem to be a way to find the current shell's limit.
|
|
961
|
+
#
|
|
880
962
|
build_prefix = f'PyMuPDF-'
|
|
963
|
+
if mupdf_version_tuple >= (1, 26):
|
|
964
|
+
# Avoid link command length problems seen on musllinux.
|
|
965
|
+
build_prefix = ''
|
|
881
966
|
if pyodide:
|
|
882
967
|
build_prefix += 'pyodide-'
|
|
883
968
|
else:
|
|
@@ -894,7 +979,6 @@ def build_mupdf_unix(
|
|
|
894
979
|
log(f'PYMUPDF_SETUP_MUPDF_TESSERACT=0 so building mupdf without tesseract.')
|
|
895
980
|
else:
|
|
896
981
|
build_prefix += 'tesseract-'
|
|
897
|
-
mupdf_version_tuple = get_mupdf_version(mupdf_local)
|
|
898
982
|
if (
|
|
899
983
|
linux
|
|
900
984
|
and os.environ.get('PYMUPDF_SETUP_MUPDF_BSYMBOLIC', '1') == '1'
|
|
@@ -1158,15 +1242,15 @@ classifier = [
|
|
|
1158
1242
|
#
|
|
1159
1243
|
|
|
1160
1244
|
# PyMuPDF version.
|
|
1161
|
-
version_p = '1.25.
|
|
1245
|
+
version_p = '1.25.5'
|
|
1162
1246
|
|
|
1163
1247
|
# PyMuPDFb version. This is the PyMuPDF version whose PyMuPDFb wheels we will
|
|
1164
1248
|
# (re)use if generating separate PyMuPDFb wheels. Though as of PyMuPDF-1.24.11
|
|
1165
|
-
# (2024-10-03) we no longer use PyMuPDFb wheels.
|
|
1249
|
+
# (2024-10-03) we no longer use PyMuPDFb wheels so this is actually unused.
|
|
1166
1250
|
#
|
|
1167
1251
|
version_b = '1.25.3'
|
|
1168
1252
|
|
|
1169
|
-
version_mupdf = '1.25.
|
|
1253
|
+
version_mupdf = '1.25.6'
|
|
1170
1254
|
|
|
1171
1255
|
if os.path.exists(f'{g_root}/{g_pymupdfb_sdist_marker}'):
|
|
1172
1256
|
|