Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,9 @@ body:
attributes:
label: PyMuPDF version
options:
- 1.26.1
- 1.26.0
- 1.25.5
- 1.25.4
- 1.25.3
- 1.25.2
- 1.25.1
- 1.25.0
- 1.24.x or earlier
- 1.25.x or earlier
- Built from source
description: |
* For example from `pymupdf.VersionBind`.
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,7 @@ jobs:
#
- uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/pymupdf*.whl
path: |
wheelhouse/pymupdf*.whl
wheelhouse/pymupdf*.tar.gz
name: artifact-${{ matrix.os }}
5 changes: 4 additions & 1 deletion changes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Change Log
==========


**Changes in version 1.26.1 ()**
**Changes in version 1.26.1**

* Use MuPDF-1.26.2.

Expand All @@ -18,6 +18,9 @@ Change Log
* New method `Document.rewrite_images()`, useful for reducing file size, changing image formats, or converting color spaces.
* `Page.get_text()`: restrict positional args to match docs.
* Removed bogus definition of class `Shape`.
* Removed release date from module, docs and changelog.
* `pymupdf.pymupdf_date` and `pymupdf.VersionDate` are now both None.
* They will be removed in a future release.


**Changes in version 1.26.0 (2025-05-22)**
Expand Down
8 changes: 3 additions & 5 deletions docs/vars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,8 @@ Constants

.. py:data:: pymupdf_date

ISO timestamp *YYYY-MM-DD HH:MM:SS* when these bindings were built.

:type: string

Disabled (set to None) in 1.26.1.

.. py:data:: version

(pymupdf_version, mupdf_version, timestamp) -- combined version information where `timestamp` is the generation point in time formatted as "YYYYMMDDhhmmss".
Expand All @@ -97,7 +95,7 @@ Constants

.. py:data:: VersionDate

Legacy equivalent to `mupdf_version`.
Disabled (set to None) in 1.26.1.


.. _PermissionCodes:
Expand Down
2 changes: 1 addition & 1 deletion docs/version.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
----

This documentation covers **PyMuPDF v1.26.0** features as of **2025-05-22 00:00:01**.
This documentation covers **PyMuPDF v1.26.1**.

The major and minor versions of |PyMuPDF| and |MuPDF| will always be the same. Only the third qualifier (patch level) may deviate from that of |MuPDF|.

Expand Down
83 changes: 56 additions & 27 deletions scripts/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
its own new venv to build PyMuPDF. Otherwise we force pip to use the
current venv.

--cibw-archs-linux <archs>
Set CIBW_ARCHS_LINUX, e.g. to `auto64 aarch64`. Default is `auto64` so
this allows control over whether to build linux-aarch64 wheels.

--cibw-name <cibw_name>
Name to use when installing cibuildwheel, e.g.:
--cibw-name cibuildwheel==3.0.0b1
Expand All @@ -75,9 +79,20 @@
...` fails with:
emcc: error: binary: No such file or directory ("binary" was expected to be an input file, based on the commandline arguments provided)

--cibw-release-1
Set up so that `cibw` builds all wheels except linux-aarch64, and sdist
if on Linux.

--cibw-release-2
Set up so that `cibw` builds only linux-aarch64 wheel.

-d
Equivalent to `-b debug`.

--dummy
Sets PYMUPDF_SETUP_DUMMY=1 which makes setup.py build a dummy wheel
with no content. For internal testing only.

-e <name>=<value>
Add to environment used in build and test commands. Can be specified
multiple times.
Expand Down Expand Up @@ -201,11 +216,6 @@

If CIBW_ARCHS is unset we set $CIBW_ARCHS_WINDOWS, $CIBW_ARCHS_MACOS
and $CIBW_ARCHS_LINUX to auto64 if they are unset.

Additionally, if running on Github ($GITHUB_ACTIONS=true) and
$CIBW_ARCHS_LINUX is unset, we set $CIBW_ARCHS_LINUX to 'auto64
aarch64' so that we build for aarch64 using emulation. This is required
as of 2025-05-23 because there is no native aarch64 host available.

install <pymupdf>
Install with `pip install --force-reinstall <pymupdf>`.
Expand Down Expand Up @@ -269,10 +279,6 @@ def main(argv):
if github_workflow_unimportant():
return

if len(argv) == 1:
show_help()
return

build_isolation = None
cibw_name = 'cibuildwheel'
cibw_pyodide = None
Expand All @@ -286,6 +292,7 @@ def main(argv):
pyodide_build_version = None
pytest_options = ''
pytest_prefix = None
cibw_sdist = None
show_args = False
show_help = False
sync_paths = False
Expand Down Expand Up @@ -330,6 +337,20 @@ def main(argv):
elif arg == '--build-isolation':
build_isolation = int(next(args))

elif arg == '--cibw-release-1':
cibw_sdist = True
env_extra['CIBW_ARCHS_LINUX'] = 'auto64'
env_extra['CIBW_ARCHS_MACOS'] = 'auto64'
env_extra['CIBW_ARCHS_WINDOWS'] = 'auto' # win32 and win64.
env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux*aarch64*'

elif arg == '--cibw-release-2':
env_extra['CIBW_ARCHS_LINUX'] = 'aarch64'
os_names = ['linux']

elif arg == '--cibw-archs-linux':
env_extra['CIBW_ARCHS_LINUX'] = next(args)

elif arg == '--cibw-name':
cibw_name = next(args)

Expand All @@ -339,6 +360,10 @@ def main(argv):
elif arg == '-d':
env_extra['PYMUPDF_SETUP_MUPDF_BUILD_TYPE'] = 'debug'

elif arg == '--dummy':
env_extra['PYMUPDF_SETUP_DUMMY'] = '1'
env_extra['CIBW_TEST_COMMAND'] = ''

elif arg == '-e':
_nv = next(args)
assert '=' in _nv, f'-e <name>=<value> does not contain "=": {_nv!r}'
Expand Down Expand Up @@ -493,7 +518,7 @@ def main(argv):

elif command == 'cibw':
# Build wheel(s) with cibuildwheel.
cibuildwheel(env_extra, cibw_name, cibw_pyodide)
cibuildwheel(env_extra, cibw_name, cibw_pyodide, cibw_sdist)

elif command.startswith('install.'):
name = command[len('install.'):]
Expand Down Expand Up @@ -629,27 +654,33 @@ def build(
run(f'pip install{build_isolation_text} -v --force-reinstall {pymupdf_dir_abs}', env_extra=env_extra)


def cibuildwheel(env_extra, cibw_name, cibw_pyodide):
def cibuildwheel(env_extra, cibw_name, cibw_pyodide, cibw_sdist):

if cibw_sdist and platform.system() == 'Linux':
log(f'Building sdist.')
run(f'cd {pymupdf_dir_abs} && {sys.executable} setup.py -d wheelhouse sdist', env_extra=env_extra)
sdists = glob.glob(f'{pymupdf_dir_abs}/wheelhouse/pymupdf-*.tar.gz')
log(f'{sdists=}')
assert sdists

run(f'pip install --upgrade {cibw_name}')

# Some general flags.
env_extra['CIBW_BUILD_VERBOSITY'] = '1'
env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux* *-win32 *-aarch64'
if 'CIBW_BUILD_VERBOSITY' not in env_extra:
env_extra['CIBW_BUILD_VERBOSITY'] = '1'
if 'CIBW_SKIP' not in env_extra:
env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux* *-win32 *-aarch64'

# Set what wheels to build, if not already specified.
if os.environ.get('CIBW_ARCHS') is None:
if os.environ.get('CIBW_ARCHS_WINDOWS') is None:
if 'CIBW_ARCHS' not in env_extra:
if 'CIBW_ARCHS_WINDOWS' not in env_extra:
env_extra['CIBW_ARCHS_WINDOWS'] = 'auto64'

if os.environ.get('CIBW_ARCHS_MACOS') is None:
if 'CIBW_ARCHS_MACOS' not in env_extra:
env_extra['CIBW_ARCHS_MACOS'] = 'auto64'

if os.environ.get('CIBW_ARCHS_LINUX') is None:
if 'CIBW_ARCHS_LINUX' not in env_extra:
env_extra['CIBW_ARCHS_LINUX'] = 'auto64'
if os.environ.get('GITHUB_ACTIONS') == 'true':
# Special case to use emulation/cross-compilation of
# aarch64 on Linux.
env_extra['CIBW_ARCHS_LINUX'] += ' aarch64'

# Tell cibuildwheel not to use `auditwheel` on Linux and MacOS,
# because it cannot cope with us deliberately having required
Expand All @@ -664,7 +695,8 @@ def cibuildwheel(env_extra, cibw_name, cibw_pyodide):
env_extra['CIBW_REPAIR_WHEEL_COMMAND_MACOS'] = ''

# Tell cibuildwheel how to test PyMuPDF.
env_extra['CIBW_TEST_COMMAND'] = f'python {{project}}/scripts/test.py test'
if 'CIBW_TEST_COMMAND' not in env_extra:
env_extra['CIBW_TEST_COMMAND'] = f'python {{project}}/scripts/test.py test'

# Specify python versions.
CIBW_BUILD = env_extra.get('CIBW_BUILD')
Expand Down Expand Up @@ -910,11 +942,8 @@ def getmtime(path):
return

pymupdf_dir_rel = gh_release.relpath(pymupdf_dir)
if pytest_options is None:
if valgrind:
pytest_options = '-s -vv'
else:
pytest_options = ''
if not pytest_options and pytest_prefix == 'valgrind':
pytest_options = '-sv'
if pytest_k:
pytest_options += f' -k {shlex.quote(pytest_k)}'
pytest_arg = ''
Expand Down
13 changes: 13 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
Location of devenv.com on Windows. If unset we search for it - see
wdev.py. if that fails we use just 'devenv.com'.

PYMUPDF_SETUP_DUMMY
If 1, we build dummy sdist and wheel with no files.

PYMUPDF_SETUP_FLAVOUR
Control building of separate wheels for PyMuPDF.

Expand Down Expand Up @@ -248,6 +251,10 @@ def run(command, check=1):
PYMUPDF_SETUP_URL_WHEEL = os.environ.get('PYMUPDF_SETUP_URL_WHEEL')
log(f'{PYMUPDF_SETUP_URL_WHEEL=}')

PYMUPDF_SETUP_DUMMY = os.environ.get('PYMUPDF_SETUP_DUMMY')
log(f'{PYMUPDF_SETUP_DUMMY=}')


def _fs_remove(path):
'''
Removes file or directory, without raising exception if it doesn't exist.
Expand Down Expand Up @@ -588,6 +595,10 @@ def build():
'''
pipcl.py `build_fn()` callback.
'''
if PYMUPDF_SETUP_DUMMY == '1':
log(f'{PYMUPDF_SETUP_DUMMY=} Building dummy wheel with no files.')
return list()

# Download MuPDF.
#
mupdf_local, mupdf_location = get_mupdf()
Expand Down Expand Up @@ -1175,6 +1186,8 @@ def _extension_flags( mupdf_local, mupdf_build_dir, build_type):

def sdist():
ret = list()
if PYMUPDF_SETUP_DUMMY == '1':
return ret

if PYMUPDF_SETUP_FLAVOUR == 'b':
# Create a minimal sdist that will build/install a dummy PyMuPDFb.
Expand Down
9 changes: 4 additions & 5 deletions src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ def _int_rc(text):

# Basic version information.
#
pymupdf_version = "1.26.0"
pymupdf_version = "1.26.1"
mupdf_version = mupdf.FZ_VERSION
pymupdf_date = "2025-05-22 00:00:01"
pymupdf_date = None

# Versions as tuples; useful when comparing versions.
#
Expand All @@ -389,11 +389,10 @@ def _int_rc(text):

# Legacy version information.
#
pymupdf_date2 = pymupdf_date.replace('-', '').replace(' ', '').replace(':', '')
version = (pymupdf_version, mupdf_version, pymupdf_date2)
version = (pymupdf_version, mupdf_version, None)
VersionFitz = mupdf_version
VersionBind = pymupdf_version
VersionDate = pymupdf_date
VersionDate = None


# String formatting.
Expand Down
12 changes: 8 additions & 4 deletions tests/resources/test_2608_expected
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
No significant gamma-ray excess above the expected background
is detected from the direction of FRB 20171019A, with 52 gamma
candidate events from the source region and 524 background event.
A second analysis using an independent event calibration and reconstruction (Parsons & Hinton 2014) confirms this result. A search for
variable emission on timescales ranging from milliseconds to several minutes with tools provided in (Brun et al. 2020) does not reveal
any variability above 2.2 𝜎. For the total data set of 1.8 h, 95% confidence level (C. L.) upper limits on the photon flux are derived using
A second analysis using an independent event calibration and recon-
struction (Parsons & Hinton 2014) confirms this result. A search for
variable emission on timescales ranging from milliseconds to sev-
eral minutes with tools provided in (Brun et al. 2020) does not reveal
any variability above 2.2 𝜎. For the total data set of 1.8 h, 95% confi-
dence level (C. L.) upper limits on the photon flux are derived using
the method described by Rolke et al. (2005). The energy threshold
of the data is highly dependent on the zenith angle of the observations. For these observations, the zenith angles range from 15 to 25
of the data is highly dependent on the zenith angle of the observa-
tions. For these observations, the zenith angles range from 15 to 25
deg, which leads to an energy threshold for the stacked data set of
𝐸th = 120 GeV. The upper limit on the Very High Energy (VHE)
10 changes: 10 additions & 0 deletions tests/resources/test_2608_expected_1.26
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
No significant gamma-ray excess above the expected background
is detected from the direction of FRB 20171019A, with 52 gamma
candidate events from the source region and 524 background event.
A second analysis using an independent event calibration and reconstruction (Parsons & Hinton 2014) confirms this result. A search for
variable emission on timescales ranging from milliseconds to several minutes with tools provided in (Brun et al. 2020) does not reveal
any variability above 2.2 𝜎. For the total data set of 1.8 h, 95% confidence level (C. L.) upper limits on the photon flux are derived using
the method described by Rolke et al. (2005). The energy threshold
of the data is highly dependent on the zenith angle of the observations. For these observations, the zenith angles range from 15 to 25
deg, which leads to an energy threshold for the stacked data set of
𝐸th = 120 GeV. The upper limit on the Very High Energy (VHE)
14 changes: 14 additions & 0 deletions tests/run_compound.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ def main():

for i in implementations:
log(f'run_compound.py: {i=}')

cpu_bits = int.bit_length(sys.maxsize+1)
log(f'{os.getcwd()=}')
log(f'{platform.machine()=}')
log(f'{platform.platform()=}')
log(f'{platform.python_version()=}')
log(f'{platform.system()=}')
log(f'{platform.uname()=}')
log(f'{sys.executable=}')
log(f'{sys.version=}')
log(f'{sys.version_info=}')
log(f'{list(sys.version_info)=}')
log(f'{cpu_bits=}')

timeout = None
if endtime:
timeout = max(0, endtime - time.time())
Expand Down
7 changes: 6 additions & 1 deletion tests/test_font.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ def test_2608():
with open(os.path.abspath(f'{__file__}/../../tests/test_2608_out'), 'wb') as f:
f.write(text.encode('utf8'))
path_expected = os.path.normpath(f'{__file__}/../../tests/resources/test_2608_expected')
with open(path_expected, 'rb') as f:
path_expected_1_26 = os.path.normpath(f'{__file__}/../../tests/resources/test_2608_expected_1.26')
if pymupdf.mupdf_version_tuple >= (1, 27):
path_expected2 = path_expected
else:
path_expected2 = path_expected_1_26
with open(path_expected2, 'rb') as f:
expected = f.read().decode('utf8')
# Github windows x32 seems to insert \r characters; maybe something to
# do with the Python installation's line endings settings.
Expand Down
5 changes: 4 additions & 1 deletion tests/test_textextract.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,10 @@ def test_4503():
strikeout = span_0['char_flags'] & pymupdf.mupdf.FZ_STEXT_STRIKEOUT
print(f'{strikeout=}')

if pymupdf.mupdf_version_tuple >= (1, 26, 2):
if pymupdf.mupdf_version_tuple >= (1, 27):
assert strikeout, f'Expected bit 0 (FZ_STEXT_STRIKEOUT) to be set in {span_0["char_flags"]=:#x}.'
assert text_0 == 'the right to request the state to review and, if appropriate,'
elif pymupdf.mupdf_version_tuple >= (1, 26, 2):
# 2025-06-09: This is still incorrect - the span should include the
# following text 'and, if appropriate,'. It looks like following spans
# are:
Expand Down
Loading