PyMuPDF 1.24.2__tar.gz → 1.24.3__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.24.2 → PyMuPDF-1.24.3}/PKG-INFO +4 -6
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/README.md +4 -2
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/READMErb.md +1 -1
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/changes.txt +30 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/pipcl.py +105 -86
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/scripts/gh_release.py +167 -143
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/scripts/sysinstall.py +3 -1
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/scripts/test.py +49 -9
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/setup.py +148 -97
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src/__init__.py +212 -488
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src/__main__.py +60 -60
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src/extra.i +3 -3
- PyMuPDF-1.24.3/src/fitz___init__.py +6 -0
- PyMuPDF-1.24.3/src/fitz_table.py +2 -0
- PyMuPDF-1.24.3/src/fitz_utils.py +2 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src/table.py +1 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src/utils.py +514 -507
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/conftest.py +4 -1
- PyMuPDF-1.24.3/tests/gentle_compare.py +26 -0
- PyMuPDF-1.24.3/tests/resources/test-2812.pdf +0 -0
- PyMuPDF-1.24.3/tests/resources/test_1645_expected_1.24.2.pdf +0 -0
- PyMuPDF-1.24.3/tests/resources/test_1645_expected_1.25.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_2548.py +51 -51
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_2634.py +5 -5
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_2791.py +4 -4
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_2904.py +9 -9
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_2907.py +5 -5
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_annots.py +56 -47
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_badfonts.py +2 -2
- PyMuPDF-1.24.3/tests/test_balance_count.py +52 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_cluster_drawings.py +12 -12
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_crypting.py +9 -9
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_drawings.py +14 -14
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_embeddedfiles.py +2 -2
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_extractimage.py +6 -6
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_flake8.py +3 -2
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_font.py +22 -22
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_general.py +171 -156
- PyMuPDF-1.24.3/tests/test_geometry.py +348 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_imagebbox.py +3 -3
- PyMuPDF-1.24.3/tests/test_import.py +18 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_insertimage.py +11 -11
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_insertpdf.py +13 -13
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_linequad.py +3 -3
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_metadata.py +3 -3
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_mupdf_regressions.py +21 -42
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_named_links.py +22 -22
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_nonpdf.py +3 -3
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_object_manipulation.py +5 -5
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_objectstreams.py +7 -7
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_optional_content.py +11 -11
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_page_links.py +2 -2
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_pagedelete.py +7 -7
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_pagelabels.py +2 -2
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_pixmap.py +47 -47
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_pylint.py +8 -4
- PyMuPDF-1.24.3/tests/test_remove-rotation.py +30 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_showpdfpage.py +11 -11
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_story.py +22 -22
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_tables.py +26 -26
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_tesseract.py +9 -9
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_textbox.py +64 -59
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_textextract.py +38 -38
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_textsearch.py +4 -4
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_toc.py +15 -15
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_widgets.py +118 -50
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_word_delimiters.py +2 -2
- PyMuPDF-1.24.2/tests/test_balance_count.py +0 -31
- PyMuPDF-1.24.2/tests/test_geometry.py +0 -348
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/COPYING +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/MANIFEST.in +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/pyproject.toml +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/pytest.ini +0 -0
- /PyMuPDF-1.24.2/src/fitz.py → /PyMuPDF-1.24.3/src/pymupdf.py +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/__init__.py +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/__main__.py +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/_config.h +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/fitz_old.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-annot.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-convert.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-defines.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-devices.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-fields.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-fileobj.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-geo-c.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-geo-py.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-globals.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-other.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-pdfinfo.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-pixmap.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-portfolio.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-python.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-select.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-stext.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/helper-xobject.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/utils.py +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/src_classic/version.i +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/README.md +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/001003ED.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/1.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/2.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/2201.00069.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/3.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/4.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/Bezier.epub +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/PragmaticaC.otf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/battery-file-22.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/bug1945.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/bug1971.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/chinese-tables.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/chinese-tables.pickle +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/circular-toc.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/cython.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/cython.pickle +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/full_toc.txt +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/github_sample.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/has-bad-fonts.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/image-file1.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/img-transparent.png +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/joined.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/metadata.txt +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/mupdf_explored.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/nur-ruhig.jpg +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/quad-calc-0.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/simple_toc.txt +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/small-table.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/strict-yes-no.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/symbol-list.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/symbols.txt +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test-2333.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test-2462.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test-3143.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test-3150.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test-3207.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test-707448.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test-707673.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test2093.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test2182.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test2238.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_1645_expected_1.22.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_1645_expected_1.24.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_1824.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2108.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2270.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2533.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2548.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2553-2.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2553.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2596.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2608_expected +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2634.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2635.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2645_1.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2645_2.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2645_3.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2710.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2730.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2742.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2788.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2791_content.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2791_coverpage.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2861.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2871.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2885.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2904.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2907.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2954.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2957_1.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2957_2.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2969.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_2979.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3058.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3062.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3070.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3072.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3087.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3179.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3186.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3197.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3357.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3362.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_3376.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_annot_file_info.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/test_delete_image.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/type3font.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/v110-changes.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/resources/widgettest.pdf +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/run_compound.py +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/tests/test_docs_samples.py +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/valgrind.supp +0 -0
- {PyMuPDF-1.24.2 → PyMuPDF-1.24.3}/wdev.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyMuPDF
|
|
3
|
-
Version: 1.24.
|
|
3
|
+
Version: 1.24.3
|
|
4
4
|
Summary: A high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents.
|
|
5
5
|
Description-Content-Type: text/markdown
|
|
6
6
|
Author: Artifex
|
|
@@ -29,6 +29,9 @@ Project-URL: Changelog, https://pymupdf.readthedocs.io/en/latest/changes.html
|
|
|
29
29
|
|
|
30
30
|
**PyMuPDF** is a high performance **Python** library for data extraction, analysis, conversion & manipulation of [PDF (and other) documents](https://pymupdf.readthedocs.io/en/latest/the-basics.html#supported-file-types).
|
|
31
31
|
|
|
32
|
+
# Community
|
|
33
|
+
Join us on **Discord** here: [#pymupdf](https://discord.gg/TSpYGBW4eq)
|
|
34
|
+
|
|
32
35
|
|
|
33
36
|
# Installation
|
|
34
37
|
|
|
@@ -78,8 +81,3 @@ Full documentation can be found on [pymupdf.readthedocs.io](https://pymupdf.read
|
|
|
78
81
|
# License and Copyright
|
|
79
82
|
|
|
80
83
|
**PyMuPDF** is available under [open-source AGPL](https://www.gnu.org/licenses/agpl-3.0.html) and commercial license agreements. If you determine you cannot meet the requirements of the **AGPL**, please contact [Artifex](https://artifex.com/contact/pymupdf-inquiry.php) for more information regarding a commercial license.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
# Contact
|
|
85
|
-
Join us on **Discord** here: [#pymupdf](https://discord.gg/TSpYGBW4eq)
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**PyMuPDF** is a high performance **Python** library for data extraction, analysis, conversion & manipulation of [PDF (and other) documents](https://pymupdf.readthedocs.io/en/latest/the-basics.html#supported-file-types).
|
|
4
4
|
|
|
5
|
+
# Community
|
|
6
|
+
Join us on **Discord** here: [#pymupdf](https://discord.gg/TSpYGBW4eq)
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
# Installation
|
|
7
10
|
|
|
@@ -54,5 +57,4 @@ Full documentation can be found on [pymupdf.readthedocs.io](https://pymupdf.read
|
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
Join us on **Discord** here: [#pymupdf](https://discord.gg/TSpYGBW4eq)
|
|
60
|
+
|
|
@@ -2,6 +2,36 @@ Change Log
|
|
|
2
2
|
==========
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
**Changes in version 1.24.3 (2024-05-09)**
|
|
6
|
+
|
|
7
|
+
*
|
|
8
|
+
The Python module is now called `pymupdf`. `fitz` is still supported for
|
|
9
|
+
backwards compatibility.
|
|
10
|
+
|
|
11
|
+
* Use MuPDF-1.24.2.
|
|
12
|
+
|
|
13
|
+
* Fixed issues:
|
|
14
|
+
|
|
15
|
+
* **Fixed** `3357 <https://github.com/pymupdf/PyMuPDF/issues/3357>`_: PyMuPDF==1.24.0 will hanging when using page.get_text("text")
|
|
16
|
+
* **Fixed** `3376 <https://github.com/pymupdf/PyMuPDF/issues/3376>`_: Redacting results are not as expected in 1.24.x.
|
|
17
|
+
* **Fixed** `3379 <https://github.com/pymupdf/PyMuPDF/issues/3379>`_: Documentation mismatch for get_text_blocks return value order.
|
|
18
|
+
* **Fixed** `3381 <https://github.com/pymupdf/PyMuPDF/issues/3381>`_: Contents stream contains floats in scientific notation
|
|
19
|
+
* **Fixed** `3402 <https://github.com/pymupdf/PyMuPDF/issues/3402>`_: Cannot add Widgets containing inter-field-calculation JavaScript
|
|
20
|
+
* **Fixed** `3414 <https://github.com/pymupdf/PyMuPDF/issues/3414>`_: missing attribute set_dpi()
|
|
21
|
+
* **Fixed** `3430 <https://github.com/pymupdf/PyMuPDF/issues/3430>`_: page.get_text() cause process freeze with certain pdf on v1.24.2
|
|
22
|
+
|
|
23
|
+
* Other:
|
|
24
|
+
|
|
25
|
+
* New/modified methods:
|
|
26
|
+
|
|
27
|
+
* `Page.remove_rotation()`: new, set page rotation to zero while keeping appearance.
|
|
28
|
+
|
|
29
|
+
* Fixed some problems when checking for PDF properties.
|
|
30
|
+
* Fixed pip builds from sdist
|
|
31
|
+
(see discussion `3360 <https://github.com/pymupdf/PyMuPDF/discussions/3360>`_:
|
|
32
|
+
Alpine linux docker build failing "No matching distribution found for pymupdfb==1.24.1").
|
|
33
|
+
|
|
34
|
+
|
|
5
35
|
**Changes in version 1.24.2 (2024-04-17)**
|
|
6
36
|
|
|
7
37
|
* Removed obsolete classic implementation from releases
|
|
@@ -78,6 +78,7 @@ class Package:
|
|
|
78
78
|
... ('cli.py', 'foo/__main__.py'),
|
|
79
79
|
... (f'build/{so_leaf}', f'foo/'),
|
|
80
80
|
... ('README', '$dist-info/'),
|
|
81
|
+
... (b'Hello world', 'foo/hw.txt'),
|
|
81
82
|
... ]
|
|
82
83
|
...
|
|
83
84
|
... def sdist():
|
|
@@ -88,6 +89,7 @@ class Package:
|
|
|
88
89
|
... 'pipcl.py',
|
|
89
90
|
... 'wdev.py',
|
|
90
91
|
... 'README',
|
|
92
|
+
... (b'Hello word2', 'hw2.txt'),
|
|
91
93
|
... ]
|
|
92
94
|
...
|
|
93
95
|
... p = pipcl.Package(
|
|
@@ -394,16 +396,18 @@ class Package:
|
|
|
394
396
|
A function taking no args, or a single `config_settings` dict
|
|
395
397
|
arg (as described in PEP-517), that builds the package.
|
|
396
398
|
|
|
397
|
-
Should return a list of items; each item should be a tuple
|
|
398
|
-
|
|
399
|
-
|
|
399
|
+
Should return a list of items; each item should be a tuple
|
|
400
|
+
`(from_, to_)`, or a single string `path` which is treated as
|
|
401
|
+
the tuple `(path, path)`.
|
|
400
402
|
|
|
401
|
-
`from_`
|
|
402
|
-
|
|
403
|
+
`from_` can be a string or a `bytes`. If a string it should
|
|
404
|
+
be the path to a file; a relative path is treated as relative
|
|
405
|
+
to `root`. If a `bytes` it is the contents of the file to be
|
|
406
|
+
added.
|
|
403
407
|
|
|
404
408
|
`to_` identifies what the file should be called within a wheel
|
|
405
409
|
or when installing. If `to_` ends with `/`, the leaf of `from_`
|
|
406
|
-
is appended to it.
|
|
410
|
+
is appended to it (and `from_` must not be a `bytes`).
|
|
407
411
|
|
|
408
412
|
Initial `$dist-info/` in `_to` is replaced by
|
|
409
413
|
`{name}-{version}.dist-info/`; this is useful for license files
|
|
@@ -437,13 +441,9 @@ class Package:
|
|
|
437
441
|
|
|
438
442
|
fn_sdist:
|
|
439
443
|
A function taking no args, or a single `config_settings` dict
|
|
440
|
-
arg (as described in PEP517), that returns a list of
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
path of a file and `to_` is its name within the sdist.
|
|
444
|
-
|
|
445
|
-
Relative paths are interpreted as relative to `root`. It is an
|
|
446
|
-
error if a path does not exist or is not a file.
|
|
444
|
+
arg (as described in PEP517), that returns a list of items to
|
|
445
|
+
be copied into the sdist. The list should be in the same format
|
|
446
|
+
as returned by `fn_build`.
|
|
447
447
|
|
|
448
448
|
It can be convenient to use `pipcl.git_items()`.
|
|
449
449
|
|
|
@@ -646,21 +646,26 @@ class Package:
|
|
|
646
646
|
record = _Record()
|
|
647
647
|
with zipfile.ZipFile(path, 'w', self.wheel_compression, self.wheel_compresslevel) as z:
|
|
648
648
|
|
|
649
|
-
def
|
|
650
|
-
|
|
651
|
-
|
|
649
|
+
def add(from_, to_):
|
|
650
|
+
if isinstance(from_, str):
|
|
651
|
+
z.write(from_, to_)
|
|
652
|
+
record.add_file(from_, to_)
|
|
653
|
+
elif isinstance(from_, bytes):
|
|
654
|
+
z.writestr(to_, from_)
|
|
655
|
+
record.add_content(from_, to_)
|
|
656
|
+
else:
|
|
657
|
+
assert 0
|
|
652
658
|
|
|
653
659
|
def add_str(content, to_):
|
|
654
|
-
|
|
655
|
-
record.add_content(content, to_)
|
|
660
|
+
add(content.encode('utf8'), to_)
|
|
656
661
|
|
|
657
662
|
dist_info_dir = self._dist_info_dir()
|
|
658
663
|
|
|
659
664
|
# Add the files returned by fn_build().
|
|
660
665
|
#
|
|
661
666
|
for item in items:
|
|
662
|
-
|
|
663
|
-
|
|
667
|
+
from_, (to_abs, to_rel) = self._fromto(item)
|
|
668
|
+
add(from_, to_rel)
|
|
664
669
|
|
|
665
670
|
# Add <name>-<version>.dist-info/WHEEL.
|
|
666
671
|
#
|
|
@@ -726,60 +731,63 @@ class Package:
|
|
|
726
731
|
else:
|
|
727
732
|
items = self.fn_sdist()
|
|
728
733
|
|
|
729
|
-
manifest = []
|
|
730
|
-
names_in_tar = []
|
|
731
|
-
def check_name(name):
|
|
732
|
-
if name in names_in_tar:
|
|
733
|
-
raise Exception(f'Name specified twice: {name}')
|
|
734
|
-
names_in_tar.append(name)
|
|
735
|
-
|
|
736
734
|
prefix = f'{self.name}-{self.version}'
|
|
737
|
-
def add_content(tar, name, contents):
|
|
738
|
-
'''
|
|
739
|
-
Adds item called `name` to `tarfile.TarInfo` `tar`, containing
|
|
740
|
-
`contents`. If contents is a string, it is encoded using utf8.
|
|
741
|
-
'''
|
|
742
|
-
log2( f'Adding: {name}')
|
|
743
|
-
if isinstance(contents, str):
|
|
744
|
-
contents = contents.encode('utf8')
|
|
745
|
-
check_name(name)
|
|
746
|
-
ti = tarfile.TarInfo(f'{prefix}/{name}')
|
|
747
|
-
ti.size = len(contents)
|
|
748
|
-
ti.mtime = time.time()
|
|
749
|
-
tar.addfile(ti, io.BytesIO(contents))
|
|
750
|
-
|
|
751
|
-
def add_file(tar, path_abs, name):
|
|
752
|
-
log2( f'Adding file: {os.path.relpath(path_abs)} => {name}')
|
|
753
|
-
check_name(name)
|
|
754
|
-
tar.add( path_abs, f'{prefix}/{name}', recursive=False)
|
|
755
|
-
|
|
756
735
|
os.makedirs(sdist_directory, exist_ok=True)
|
|
757
736
|
tarpath = f'{sdist_directory}/{prefix}.tar.gz'
|
|
758
737
|
log2(f'Creating sdist: {tarpath}')
|
|
738
|
+
|
|
759
739
|
with tarfile.open(tarpath, 'w:gz') as tar:
|
|
740
|
+
|
|
741
|
+
names_in_tar = list()
|
|
742
|
+
def check_name(name):
|
|
743
|
+
if name in names_in_tar:
|
|
744
|
+
raise Exception(f'Name specified twice: {name}')
|
|
745
|
+
names_in_tar.append(name)
|
|
746
|
+
|
|
747
|
+
def add(from_, name):
|
|
748
|
+
check_name(name)
|
|
749
|
+
if isinstance(from_, str):
|
|
750
|
+
log2( f'Adding file: {os.path.relpath(from_)} => {name}')
|
|
751
|
+
tar.add( from_, f'{prefix}/{name}', recursive=False)
|
|
752
|
+
elif isinstance(from_, bytes):
|
|
753
|
+
log2( f'Adding: {name}')
|
|
754
|
+
ti = tarfile.TarInfo(f'{prefix}/{name}')
|
|
755
|
+
ti.size = len(from_)
|
|
756
|
+
ti.mtime = time.time()
|
|
757
|
+
tar.addfile(ti, io.BytesIO(from_))
|
|
758
|
+
else:
|
|
759
|
+
assert 0
|
|
760
|
+
|
|
761
|
+
def add_string(text, name):
|
|
762
|
+
textb = text.encode('utf8')
|
|
763
|
+
return add(textb, name)
|
|
764
|
+
|
|
760
765
|
found_pyproject_toml = False
|
|
761
766
|
for item in items:
|
|
762
|
-
|
|
763
|
-
if
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
767
|
+
from_, (to_abs, to_rel) = self._fromto(item)
|
|
768
|
+
if isinstance(from_, bytes):
|
|
769
|
+
add(from_, to_rel)
|
|
770
|
+
else:
|
|
771
|
+
if from_.startswith(f'{os.path.abspath(sdist_directory)}/'):
|
|
772
|
+
# Source files should not be inside <sdist_directory>.
|
|
773
|
+
assert 0, f'Path is inside sdist_directory={sdist_directory}: {from_!r}'
|
|
774
|
+
assert os.path.exists(from_), f'Path does not exist: {from_!r}'
|
|
775
|
+
assert os.path.isfile(from_), f'Path is not a file: {from_!r}'
|
|
776
|
+
if to_rel == 'pyproject.toml':
|
|
777
|
+
found_pyproject_toml = True
|
|
778
|
+
add(from_, to_rel)
|
|
779
|
+
|
|
772
780
|
if not found_pyproject_toml:
|
|
773
781
|
log0(f'Warning: no pyproject.toml specified.')
|
|
774
782
|
|
|
775
783
|
# Always add a PKG-INFO file.
|
|
776
|
-
|
|
784
|
+
add_string(self._metainfo(), 'PKG-INFO')
|
|
777
785
|
|
|
778
786
|
if self.license:
|
|
779
787
|
if 'COPYING' in names_in_tar:
|
|
780
788
|
log2(f'Not writing .license because file already in sdist: COPYING')
|
|
781
789
|
else:
|
|
782
|
-
|
|
790
|
+
add_string(self.license, 'COPYING')
|
|
783
791
|
|
|
784
792
|
log1( f'Have created sdist: {tarpath}')
|
|
785
793
|
return os.path.basename(tarpath)
|
|
@@ -849,11 +857,18 @@ class Package:
|
|
|
849
857
|
record_path = f'{root2}/{dist_info_dir}/RECORD'
|
|
850
858
|
record = _Record()
|
|
851
859
|
|
|
852
|
-
def add_file(
|
|
853
|
-
log2(f'Copying from {from_rel} to {to_abs}')
|
|
860
|
+
def add_file(from_, to_abs, to_rel):
|
|
854
861
|
os.makedirs( os.path.dirname( to_abs), exist_ok=True)
|
|
855
|
-
|
|
856
|
-
|
|
862
|
+
if isinstance(from_, bytes):
|
|
863
|
+
log2(f'Copying content into {to_abs}.')
|
|
864
|
+
with open(to_abs, 'wb') as f:
|
|
865
|
+
f.write(from_)
|
|
866
|
+
record.add_content(from_, to_rel)
|
|
867
|
+
else:
|
|
868
|
+
log0(f'{from_=}')
|
|
869
|
+
log2(f'Copying from {os.path.relpath(from_, self.root)} to {to_abs}')
|
|
870
|
+
shutil.copy2( from_, to_abs)
|
|
871
|
+
record.add_file(from_, to_rel)
|
|
857
872
|
|
|
858
873
|
def add_str(content, to_abs, to_rel):
|
|
859
874
|
log2( f'Writing to: {to_abs}')
|
|
@@ -863,9 +878,10 @@ class Package:
|
|
|
863
878
|
record.add_content(content, to_rel)
|
|
864
879
|
|
|
865
880
|
for item in items:
|
|
866
|
-
|
|
881
|
+
from_, (to_abs, to_rel) = self._fromto(item)
|
|
882
|
+
log0(f'{from_=} {to_abs=} {to_rel=}')
|
|
867
883
|
to_abs2 = f'{root2}/{to_rel}'
|
|
868
|
-
add_file(
|
|
884
|
+
add_file( from_, to_abs2, to_rel)
|
|
869
885
|
|
|
870
886
|
add_str( self._metainfo(), f'{root2}/{dist_info_dir}/METADATA', f'{dist_info_dir}/METADATA')
|
|
871
887
|
|
|
@@ -1245,12 +1261,14 @@ class Package:
|
|
|
1245
1261
|
|
|
1246
1262
|
def _fromto(self, p):
|
|
1247
1263
|
'''
|
|
1248
|
-
Returns `(
|
|
1264
|
+
Returns `(from_, (to_abs, to_rel))`.
|
|
1249
1265
|
|
|
1250
|
-
If `p` is a string we convert to `(p, p)`. Otherwise we assert
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1266
|
+
If `p` is a string we convert to `(p, p)`. Otherwise we assert that
|
|
1267
|
+
`p` is a tuple `(from_, to_)` where `from_` is str/bytes and `to_` is
|
|
1268
|
+
str. If `from_` is a bytes it is contents of file to add, otherwise the
|
|
1269
|
+
path of an existing file; non-absolute paths are assumed to be relative
|
|
1270
|
+
to `self.root`. If `to_` is empty or ends with `/`, we append the leaf
|
|
1271
|
+
of `from_` (which must be a str).
|
|
1254
1272
|
|
|
1255
1273
|
If `to_` starts with `$dist-info/`, we replace this with
|
|
1256
1274
|
`self._dist_info_dir()`.
|
|
@@ -1258,21 +1276,18 @@ class Package:
|
|
|
1258
1276
|
If `to_` starts with `$data/`, we replace this with
|
|
1259
1277
|
`{self.name}-{self.version}.data/`.
|
|
1260
1278
|
|
|
1261
|
-
|
|
1262
|
-
`within self.root`.
|
|
1279
|
+
We assert that `to_abs` is `within self.root`.
|
|
1263
1280
|
|
|
1264
|
-
`
|
|
1265
|
-
`relative to self.root`.
|
|
1281
|
+
`to_rel` is derived from the `to_abs` and is relative to self.root`.
|
|
1266
1282
|
'''
|
|
1267
1283
|
ret = None
|
|
1268
1284
|
if isinstance(p, str):
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
assert
|
|
1275
|
-
from_, to_ = ret
|
|
1285
|
+
p = p, p
|
|
1286
|
+
assert isinstance(p, tuple) and len(p) == 2
|
|
1287
|
+
|
|
1288
|
+
from_, to_ = p
|
|
1289
|
+
assert isinstance(from_, (str, bytes))
|
|
1290
|
+
assert isinstance(to_, str)
|
|
1276
1291
|
if to_.endswith('/') or to_=='':
|
|
1277
1292
|
to_ += os.path.basename(from_)
|
|
1278
1293
|
prefix = '$dist-info/'
|
|
@@ -1281,8 +1296,11 @@ class Package:
|
|
|
1281
1296
|
prefix = '$data/'
|
|
1282
1297
|
if to_.startswith( prefix):
|
|
1283
1298
|
to_ = f'{self.name}-{self.version}.data/{to_[ len(prefix):]}'
|
|
1284
|
-
|
|
1299
|
+
if isinstance(from_, str):
|
|
1300
|
+
from_, _ = self._path_relative_to_root( from_, assert_within_root=False)
|
|
1285
1301
|
to_ = self._path_relative_to_root(to_)
|
|
1302
|
+
assert isinstance(from_, (str, bytes))
|
|
1303
|
+
log2(f'returning {from_=} {to_=}')
|
|
1286
1304
|
return from_, to_
|
|
1287
1305
|
|
|
1288
1306
|
|
|
@@ -2212,7 +2230,7 @@ def _fs_mtime( filename, default=0):
|
|
|
2212
2230
|
except OSError:
|
|
2213
2231
|
return default
|
|
2214
2232
|
|
|
2215
|
-
g_verbose = int(os.environ.get('PIPCL_VERBOSE', '
|
|
2233
|
+
g_verbose = int(os.environ.get('PIPCL_VERBOSE', '1'))
|
|
2216
2234
|
|
|
2217
2235
|
def verbose(level=None):
|
|
2218
2236
|
'''
|
|
@@ -2324,7 +2342,7 @@ class _Record:
|
|
|
2324
2342
|
def __init__(self):
|
|
2325
2343
|
self.text = ''
|
|
2326
2344
|
|
|
2327
|
-
def add_content(self, content, to_):
|
|
2345
|
+
def add_content(self, content, to_, verbose=True):
|
|
2328
2346
|
if isinstance(content, str):
|
|
2329
2347
|
content = content.encode('utf8')
|
|
2330
2348
|
|
|
@@ -2339,13 +2357,14 @@ class _Record:
|
|
|
2339
2357
|
digest = digest.decode('utf8')
|
|
2340
2358
|
|
|
2341
2359
|
self.text += f'{to_},sha256={digest},{len(content)}\n'
|
|
2342
|
-
|
|
2360
|
+
if verbose:
|
|
2361
|
+
log2(f'Adding {to_}')
|
|
2343
2362
|
|
|
2344
2363
|
def add_file(self, from_, to_):
|
|
2364
|
+
log2(f'Adding file: {os.path.relpath(from_)} => {to_}')
|
|
2345
2365
|
with open(from_, 'rb') as f:
|
|
2346
2366
|
content = f.read()
|
|
2347
|
-
self.add_content(content, to_)
|
|
2348
|
-
log2(f'Adding file: {os.path.relpath(from_)} => {to_}')
|
|
2367
|
+
self.add_content(content, to_, verbose=False)
|
|
2349
2368
|
|
|
2350
2369
|
def get(self, record_path=None):
|
|
2351
2370
|
'''
|