diff --git a/src/ocrmypdf/subprocess/_windows.py b/src/ocrmypdf/subprocess/_windows.py index 5b983fde..3a6c2a39 100644 --- a/src/ocrmypdf/subprocess/_windows.py +++ b/src/ocrmypdf/subprocess/_windows.py @@ -103,12 +103,12 @@ def registry_path_tesseract(env=None) -> Iterator[Path]: log.warning(e) -def _program_version_in_path_key(path: Path) -> tuple[str, Version | None]: +def _gs_version_in_path_key(path: Path) -> tuple[str, Version | None]: """Key function for comparing Ghostscript and Tesseract paths. Ghostscript installs on Windows: - %PROGRAMFILES%/gs/gs9.56.1 -> ('gs', Version('9.56.1')) - %PROGRAMFILES%/gs/gs9.24 -> ('gs', Version('9.24')) + %PROGRAMFILES%/gs/gs9.56.1/bin -> ('gs', Version('9.56.1')) + %PROGRAMFILES%/gs/9.24/bin -> ('gs', Version('9.24')) Tesseract looks like: %PROGRAMFILES%/Tesseract-OCR -> ('Tesseract-OCR', None) @@ -116,13 +116,12 @@ def _program_version_in_path_key(path: Path) -> tuple[str, Version | None]: Thus ensuring the resulting tuple will order the alternatives correctly, e.g. gs10.0 > gs9.99. """ - match = re.match(r'([^0-9]+)(.*)', str(path.name)) + match = re.search(r'gs[/\\]?([0-9.]+)[/\\]bin', str(path)) if match: try: - program = match.group(1) - version_str = match.group(2) + version_str = match.group(1) version = Version(version_str) - return program, version + return 'gs', version except InvalidVersion: pass return path.name, None @@ -145,7 +144,7 @@ def program_files_paths(env=None) -> Iterator[Path]: return iter( sorted( (p for p in path_walker()), - key=_program_version_in_path_key, + key=_gs_version_in_path_key, reverse=True, ) ) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 5d5f5d1e..bd43b5b5 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -6,9 +6,11 @@ from __future__ import annotations import logging import multiprocessing import os +from pathlib import Path from unittest.mock import MagicMock import pytest +from packaging.version import Version from ocrmypdf import helpers @@ -100,6 +102,17 @@ class TestFileIsWritable: assert not helpers.is_file_writable(pathmock) +@pytest.mark.skipif(os.name != 'nt', reason="Windows test") +def test_gs_install_locations(): + # pylint: disable=import-outside-toplevel + from ocrmypdf.subprocess._windows import _gs_version_in_path_key + + assert _gs_version_in_path_key(Path("C:\\Program Files\\gs\\gs9.52\\bin")) == ( + 'gs', + Version('9.52'), + ) + + @pytest.mark.skipif(os.name != 'nt', reason="Windows test") def test_shim_paths(tmp_path): # pylint: disable=import-outside-toplevel @@ -109,7 +122,7 @@ def test_shim_paths(tmp_path): progfiles.mkdir() (progfiles / 'tesseract-ocr').mkdir() (progfiles / 'gs' / '9.51' / 'bin').mkdir(parents=True) - (progfiles / 'gs' / '9.52' / 'bin').mkdir(parents=True) + (progfiles / 'gs' / 'gs9.52.3' / 'bin').mkdir(parents=True) syspath = tmp_path / 'bin' env = {'PROGRAMFILES': str(progfiles), 'PATH': str(syspath)} @@ -117,7 +130,7 @@ def test_shim_paths(tmp_path): results = result_str.split(os.pathsep) assert results[0] == str(syspath), results assert results[-3].endswith('tesseract-ocr'), results - assert results[-2].endswith(os.path.join('gs', '9.52', 'bin')), results + assert results[-2].endswith(os.path.join('gs9.52.3', 'bin')), results assert results[-1].endswith(os.path.join('gs', '9.51', 'bin')), results