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.
Files changed (273) hide show
  1. {pymupdf-1.25.3 → pymupdf-1.25.5}/PKG-INFO +1 -1
  2. {pymupdf-1.25.3 → pymupdf-1.25.5}/changes.txt +46 -2
  3. {pymupdf-1.25.3 → pymupdf-1.25.5}/scripts/sysinstall.py +9 -2
  4. {pymupdf-1.25.3 → pymupdf-1.25.5}/scripts/test.py +77 -99
  5. {pymupdf-1.25.3 → pymupdf-1.25.5}/setup.py +97 -13
  6. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/__init__.py +99 -45
  7. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/_apply_pages.py +1 -1
  8. pymupdf-1.25.5/src/_wxcolors.py +562 -0
  9. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/extra.i +154 -33
  10. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/fitz___init__.py +1 -0
  11. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/table.py +1 -2
  12. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/utils.py +275 -625
  13. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/fitz_old.i +11 -3
  14. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-python.i +1 -1
  15. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/utils.py +9 -1
  16. pymupdf-1.25.5/tests/resources/merge-form1.pdf +0 -0
  17. pymupdf-1.25.5/tests/resources/merge-form2.pdf +0 -0
  18. pymupdf-1.25.5/tests/resources/test_3886.pdf +0 -0
  19. pymupdf-1.25.5/tests/resources/test_3887.pdf +0 -0
  20. pymupdf-1.25.5/tests/resources/test_4079_after.pdf +0 -0
  21. pymupdf-1.25.5/tests/resources/test_4224.pdf +0 -0
  22. pymupdf-1.25.5/tests/resources/test_4263.pdf +0 -0
  23. pymupdf-1.25.5/tests/resources/test_4415.pdf +0 -0
  24. pymupdf-1.25.5/tests/resources/test_4415_out_expected.png +0 -0
  25. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_annots.py +31 -12
  26. pymupdf-1.25.5/tests/test_barcode.py +63 -0
  27. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_codespell.py +3 -9
  28. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_font.py +23 -2
  29. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_general.py +94 -4
  30. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_insertpdf.py +84 -7
  31. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pixmap.py +52 -2
  32. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pylint.py +1 -0
  33. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_story.py +3 -1
  34. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_textbox.py +11 -1
  35. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_textextract.py +6 -4
  36. {pymupdf-1.25.3 → pymupdf-1.25.5}/COPYING +0 -0
  37. {pymupdf-1.25.3 → pymupdf-1.25.5}/README.md +0 -0
  38. {pymupdf-1.25.3 → pymupdf-1.25.5}/READMEb.md +0 -0
  39. {pymupdf-1.25.3 → pymupdf-1.25.5}/READMEd.md +0 -0
  40. {pymupdf-1.25.3 → pymupdf-1.25.5}/pipcl.py +0 -0
  41. {pymupdf-1.25.3 → pymupdf-1.25.5}/pyproject.toml +0 -0
  42. {pymupdf-1.25.3 → pymupdf-1.25.5}/pytest.ini +0 -0
  43. {pymupdf-1.25.3 → pymupdf-1.25.5}/scripts/gh_release.py +0 -0
  44. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/__main__.py +0 -0
  45. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/fitz_table.py +0 -0
  46. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/fitz_utils.py +0 -0
  47. {pymupdf-1.25.3 → pymupdf-1.25.5}/src/pymupdf.py +0 -0
  48. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/__init__.py +0 -0
  49. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/__main__.py +0 -0
  50. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/_config.h +0 -0
  51. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-annot.i +0 -0
  52. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-convert.i +0 -0
  53. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-defines.i +0 -0
  54. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-devices.i +0 -0
  55. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-fields.i +0 -0
  56. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-fileobj.i +0 -0
  57. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-geo-c.i +0 -0
  58. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-geo-py.i +0 -0
  59. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-globals.i +0 -0
  60. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-other.i +0 -0
  61. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-pdfinfo.i +0 -0
  62. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-pixmap.i +0 -0
  63. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-portfolio.i +0 -0
  64. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-select.i +0 -0
  65. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-stext.i +0 -0
  66. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/helper-xobject.i +0 -0
  67. {pymupdf-1.25.3 → pymupdf-1.25.5}/src_classic/version.i +0 -0
  68. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/README.md +0 -0
  69. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/conftest.py +0 -0
  70. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/gentle_compare.py +0 -0
  71. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/001003ED.pdf +0 -0
  72. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/1.pdf +0 -0
  73. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/2.pdf +0 -0
  74. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/2201.00069.pdf +0 -0
  75. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/3.pdf +0 -0
  76. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/4.pdf +0 -0
  77. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/Bezier.epub +0 -0
  78. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/PragmaticaC.otf +0 -0
  79. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/battery-file-22.pdf +0 -0
  80. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/bug1945.pdf +0 -0
  81. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/bug1971.pdf +0 -0
  82. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/chinese-tables.pdf +0 -0
  83. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/chinese-tables.pickle +0 -0
  84. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/circular-toc.pdf +0 -0
  85. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/cms-etc-filled.pdf +0 -0
  86. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/cython.pdf +0 -0
  87. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/cython.pickle +0 -0
  88. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/dotted-gridlines.pdf +0 -0
  89. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/full_toc.txt +0 -0
  90. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/github_sample.pdf +0 -0
  91. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/has-bad-fonts.pdf +0 -0
  92. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/image-file1.pdf +0 -0
  93. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/img-regular.pdf +0 -0
  94. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/img-transparent.pdf +0 -0
  95. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/img-transparent.png +0 -0
  96. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/interfield-calculation.pdf +0 -0
  97. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/joined.pdf +0 -0
  98. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/metadata.txt +0 -0
  99. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/mupdf_explored.pdf +0 -0
  100. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/nur-ruhig.jpg +0 -0
  101. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/quad-calc-0.pdf +0 -0
  102. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/simple_toc.txt +0 -0
  103. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/small-table.pdf +0 -0
  104. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/strict-yes-no.pdf +0 -0
  105. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/symbol-list.pdf +0 -0
  106. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/symbols.txt +0 -0
  107. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-2333.pdf +0 -0
  108. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-2462.pdf +0 -0
  109. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-2812.pdf +0 -0
  110. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3143.pdf +0 -0
  111. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3150.pdf +0 -0
  112. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3207.pdf +0 -0
  113. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3591.pdf +0 -0
  114. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-3820.pdf +0 -0
  115. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-4055.pdf +0 -0
  116. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-707448.pdf +0 -0
  117. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-707673.pdf +0 -0
  118. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-E+A.pdf +0 -0
  119. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test-linebreaks.pdf +0 -0
  120. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test2093.pdf +0 -0
  121. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test2182.pdf +0 -0
  122. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test2238.pdf +0 -0
  123. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_1645_expected.pdf +0 -0
  124. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_1824.pdf +0 -0
  125. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2108.pdf +0 -0
  126. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2270.pdf +0 -0
  127. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2533.pdf +0 -0
  128. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2548.pdf +0 -0
  129. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2553-2.pdf +0 -0
  130. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2553.pdf +0 -0
  131. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2596.pdf +0 -0
  132. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2608_expected +0 -0
  133. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2634.pdf +0 -0
  134. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2635.pdf +0 -0
  135. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2645_1.pdf +0 -0
  136. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2645_2.pdf +0 -0
  137. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2645_3.pdf +0 -0
  138. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2710.pdf +0 -0
  139. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2730.pdf +0 -0
  140. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2742.pdf +0 -0
  141. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2788.pdf +0 -0
  142. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2791_content.pdf +0 -0
  143. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2791_coverpage.pdf +0 -0
  144. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2861.pdf +0 -0
  145. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2871.pdf +0 -0
  146. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2885.pdf +0 -0
  147. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2904.pdf +0 -0
  148. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2907.pdf +0 -0
  149. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2954.pdf +0 -0
  150. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2957_1.pdf +0 -0
  151. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2957_2.pdf +0 -0
  152. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2969.pdf +0 -0
  153. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_2979.pdf +0 -0
  154. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3050_expected.png +0 -0
  155. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3058.pdf +0 -0
  156. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3062.pdf +0 -0
  157. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3070.pdf +0 -0
  158. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3072.pdf +0 -0
  159. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3087.pdf +0 -0
  160. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3179.pdf +0 -0
  161. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3186.pdf +0 -0
  162. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3197.pdf +0 -0
  163. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3357.pdf +0 -0
  164. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3362.pdf +0 -0
  165. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3376.pdf +0 -0
  166. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3448.pdf +0 -0
  167. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3448.pdf-expected.png +0 -0
  168. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3450.pdf +0 -0
  169. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3493.epub +0 -0
  170. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3569.pdf +0 -0
  171. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3594.pdf +0 -0
  172. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3615.epub +0 -0
  173. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3624.pdf +0 -0
  174. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3624_expected.png +0 -0
  175. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3650.pdf +0 -0
  176. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3654.docx +0 -0
  177. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3677.pdf +0 -0
  178. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3687-3.epub +0 -0
  179. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3687.epub +0 -0
  180. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3705.pdf +0 -0
  181. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3725.pdf +0 -0
  182. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3727.pdf +0 -0
  183. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3780.pdf +0 -0
  184. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3789.pdf +0 -0
  185. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3842.pdf +0 -0
  186. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3848.pdf +0 -0
  187. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3854.pdf +0 -0
  188. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3854_expected.png +0 -0
  189. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf +0 -0
  190. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.0.png +0 -0
  191. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.1.png +0 -0
  192. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.2.png +0 -0
  193. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.3.png +0 -0
  194. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.4.png +0 -0
  195. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.5.png +0 -0
  196. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.6.png +0 -0
  197. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3863.pdf.pdf.7.png +0 -0
  198. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3933.pdf +0 -0
  199. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3950.pdf +0 -0
  200. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_3994.pdf +0 -0
  201. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4004.pdf +0 -0
  202. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4017.pdf +0 -0
  203. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4026.pdf +0 -0
  204. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4034.pdf +0 -0
  205. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4043.pdf +0 -0
  206. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4047.pdf +0 -0
  207. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4079.pdf +0 -0
  208. /pymupdf-1.25.3/tests/resources/test_4079_after.pdf → /pymupdf-1.25.5/tests/resources/test_4079_after_1.25.pdf +0 -0
  209. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4090.pdf +0 -0
  210. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4125.pdf +0 -0
  211. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4139.pdf +0 -0
  212. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4141.pdf +0 -0
  213. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4147.pdf +0 -0
  214. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4179.pdf +0 -0
  215. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4179_expected.png +0 -0
  216. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4180.pdf +0 -0
  217. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4180_expected.png +0 -0
  218. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4182.pdf +0 -0
  219. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4182_expected.png +0 -0
  220. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4245.pdf +0 -0
  221. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_4245_expected.png +0 -0
  222. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_annot_file_info.pdf +0 -0
  223. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_delete_image.pdf +0 -0
  224. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/test_toc_count.pdf +0 -0
  225. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/text-find-ligatures.pdf +0 -0
  226. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/type3font.pdf +0 -0
  227. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/v110-changes.pdf +0 -0
  228. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/resources/widgettest.pdf +0 -0
  229. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/run_compound.py +0 -0
  230. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2548.py +0 -0
  231. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2634.py +0 -0
  232. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2904.py +0 -0
  233. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_2907.py +0 -0
  234. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_4141.py +0 -0
  235. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_badfonts.py +0 -0
  236. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_balance_count.py +0 -0
  237. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_cluster_drawings.py +0 -0
  238. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_crypting.py +0 -0
  239. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_docs_samples.py +0 -0
  240. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_drawings.py +0 -0
  241. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_embeddedfiles.py +0 -0
  242. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_extractimage.py +0 -0
  243. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_flake8.py +0 -0
  244. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_geometry.py +0 -0
  245. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_imagebbox.py +0 -0
  246. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_imagemasks.py +0 -0
  247. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_import.py +0 -0
  248. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_insertimage.py +0 -0
  249. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_linebreaks.py +0 -0
  250. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_linequad.py +0 -0
  251. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_memory.py +0 -0
  252. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_metadata.py +0 -0
  253. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_mupdf_regressions.py +0 -0
  254. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_named_links.py +0 -0
  255. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_nonpdf.py +0 -0
  256. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_object_manipulation.py +0 -0
  257. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_objectstreams.py +0 -0
  258. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_optional_content.py +0 -0
  259. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_page_links.py +0 -0
  260. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pagedelete.py +0 -0
  261. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_pagelabels.py +0 -0
  262. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_remove-rotation.py +0 -0
  263. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_rtl.py +0 -0
  264. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_showpdfpage.py +0 -0
  265. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_spikes.py +0 -0
  266. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_tables.py +0 -0
  267. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_tesseract.py +0 -0
  268. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_textsearch.py +0 -0
  269. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_toc.py +0 -0
  270. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_widgets.py +0 -0
  271. {pymupdf-1.25.3 → pymupdf-1.25.5}/tests/test_word_delimiters.py +0 -0
  272. {pymupdf-1.25.3 → pymupdf-1.25.5}/valgrind.supp +0 -0
  273. {pymupdf-1.25.3 → pymupdf-1.25.5}/wdev.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyMuPDF
3
- Version: 1.25.3
3
+ Version: 1.25.5
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
@@ -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 /Resouces object
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 supression of spikes caused by long miters.
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
- args = iter(sys.argv[1:])
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/PyMuPDF-*.dist-info || true')
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
- build(
280
- build_type=build_type,
281
- build_isolation=build_isolation,
282
- venv_quick=venv_quick,
283
- build_mupdf=build_mupdf,
284
- build_flavour=build_flavour,
285
- wheel=wheel,
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 pyinit'
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
- * Clone emsdk repository to `pipcl_emsdk` if not already present.
504
- * Create and activate a venv `pipcl_venv_pyodide` if not already present.
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 = pipcl_wasm.pyodide_setup()
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 "### cloning emsdk.git"'
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
- # 2024-10-11: we only work with python-3.11; later versions fail with
533
- # pyodide-build==0.23.4 because `distutils` not available.
534
- if pyodide_build_version:
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 "### creating venv {venv_pyodide}"'
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 "### running pip install ..."'
550
- command += f' && python -m pip install --upgrade pip wheel pyodide-build=={pyodide_build_version}'
551
- #command += f' && python -m pip install --upgrade pip wheel pyodide-build'
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 "### running ./emsdk install"'
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 "### running ./emsdk activate"'
594
+ command += ' && echo "### Running ./emsdk activate"'
560
595
  command += ' && ./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}'
561
- command += ' && echo "### running ./emsdk_env.sh"'
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
- g_py_limited_api = (PYMUPDF_SETUP_PY_LIMITED_API != '0')
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 get_git_id( directory):
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'get_git_id(): directory={directory!r} returning branch={branch!r} sha={sha!r} comment={comment!r}')
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
- run(f'git clone --recursive --depth 1 --shallow-submodules {location[4:]} {local_dir}')
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.3'
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.4'
1253
+ version_mupdf = '1.25.6'
1170
1254
 
1171
1255
  if os.path.exists(f'{g_root}/{g_pymupdfb_sdist_marker}'):
1172
1256