diff --git a/src/ocrmypdf/_pipeline.py b/src/ocrmypdf/_pipeline.py
index 0806880e..6e67d808 100644
--- a/src/ocrmypdf/_pipeline.py
+++ b/src/ocrmypdf/_pipeline.py
@@ -321,9 +321,9 @@ def rasterize_preview(input_file, page_context):
output_file = page_context.get_path('rasterize_preview.jpg')
canvas_dpi = get_canvas_square_dpi(page_context.pageinfo, page_context.options)
page_dpi = get_page_square_dpi(page_context.pageinfo, page_context.options)
- ghostscript.rasterize_pdf(
- input_file,
- output_file,
+ page_context.plugin_manager.hook.rasterize_pdf_page(
+ input_file=input_file,
+ output_file=output_file,
raster_device='jpeggray',
raster_dpi=canvas_dpi,
page_dpi=page_dpi,
@@ -430,9 +430,9 @@ def rasterize(
canvas_dpi = get_canvas_square_dpi(pageinfo, page_context.options)
page_dpi = get_page_square_dpi(pageinfo, page_context.options)
- ghostscript.rasterize_pdf(
- input_file,
- output_file,
+ page_context.plugin_manager.hook.rasterize_pdf_page(
+ input_file=input_file,
+ output_file=output_file,
raster_device=device,
raster_dpi=canvas_dpi,
page_dpi=page_dpi,
diff --git a/src/ocrmypdf/_plugin_manager.py b/src/ocrmypdf/_plugin_manager.py
index 1fbc1b76..9f328c69 100644
--- a/src/ocrmypdf/_plugin_manager.py
+++ b/src/ocrmypdf/_plugin_manager.py
@@ -33,7 +33,10 @@ def get_plugin_manager(plugins: List[str], builtins=True):
pm.add_hookspecs(pluginspec)
if builtins:
- all_plugins = ['ocrmypdf.builtin_plugins'] + plugins
+ all_plugins = [
+ 'ocrmypdf.builtin_plugins.ghostscript',
+ 'ocrmypdf.builtin_plugins.tesseract_ocr',
+ ] + plugins
else:
all_plugins = plugins
for name in all_plugins:
diff --git a/src/ocrmypdf/_validation.py b/src/ocrmypdf/_validation.py
index 941e3522..3a92d34c 100644
--- a/src/ocrmypdf/_validation.py
+++ b/src/ocrmypdf/_validation.py
@@ -27,7 +27,6 @@ from shutil import copyfileobj
import PIL
-from ocrmypdf._plugin_manager import get_plugin_manager
from ocrmypdf._unicodefun import verify_python3_env
from ocrmypdf.exceptions import (
BadArgsError,
@@ -35,14 +34,7 @@ from ocrmypdf.exceptions import (
MissingDependencyError,
OutputFileAccessError,
)
-from ocrmypdf.exec import (
- check_external_program,
- ghostscript,
- jbig2enc,
- pngquant,
- tesseract,
- unpaper,
-)
+from ocrmypdf.exec import check_external_program, jbig2enc, pngquant, unpaper
from ocrmypdf.helpers import (
is_file_writable,
is_iterable_notstr,
@@ -92,10 +84,6 @@ def check_options_languages(options, plugin_manager):
def check_options_output(options):
- # We have these constraints to check for.
- # 1. Ghostscript < 9.20 mangles multibyte Unicode
- # 2. hocr doesn't work on non-Latin languages (so don't select it)
-
is_latin = options.languages.issubset(HOCR_OK_LANGS)
if options.pdf_renderer == 'hocr' and not is_latin:
@@ -106,25 +94,6 @@ def check_options_output(options):
)
log.warning(msg)
- if ghostscript.version() < '9.20' and options.output_type != 'pdf' and not is_latin:
- # https://bugs.ghostscript.com/show_bug.cgi?id=696874
- # Ghostscript < 9.20 fails to encode multibyte characters properly
- msg = (
- "The installed version of Ghostscript does not work correctly "
- "with the OCR languages you specified. Use --output-type pdf or "
- "upgrade to Ghostscript 9.20 or later to avoid this issue."
- )
- msg += f"Found Ghostscript {ghostscript.version()}"
- log.warning(msg)
-
- if options.output_type == 'pdfa':
- options.output_type = 'pdfa-2'
-
- if options.output_type == 'pdfa-3' and ghostscript.version() < '9.19':
- raise MissingDependencyError(
- "--output-type pdfa-3 requires Ghostscript 9.19 or later"
- )
-
lossless_reconstruction = False
if not any(
(
@@ -291,7 +260,6 @@ def check_options(options, plugin_manager):
check_options_optimizing(options)
check_options_advanced(options)
check_options_pillow(options)
- check_dependency_versions(options)
plugin_manager.hook.check_options(options=options)
@@ -432,19 +400,3 @@ def report_output_file_size(options, input_file, output_file):
f"The output file size is {ratio:.2f}× larger than the input file.\n"
f"{explanation}"
)
-
-
-def check_dependency_versions(options):
- check_external_program(
- program='gs',
- package='ghostscript',
- version_checker=ghostscript.version,
- need_version='9.15', # limited by Travis CI / Ubuntu 14.04 backports
- )
- gs_version = ghostscript.version()
- if gs_version in ('9.24', '9.51'):
- raise MissingDependencyError(
- f"Ghostscript {gs_version} contains serious regressions and is not "
- "supported. Please upgrade to a newer version, or downgrade to the "
- "previous version."
- )
diff --git a/src/ocrmypdf/builtin_plugins/__init__.py b/src/ocrmypdf/builtin_plugins/__init__.py
index e5fd494e..0ed32bc2 100644
--- a/src/ocrmypdf/builtin_plugins/__init__.py
+++ b/src/ocrmypdf/builtin_plugins/__init__.py
@@ -14,5 +14,3 @@
#
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see .
-
-from ocrmypdf.builtin_plugins.tesseract_ocr import *
diff --git a/src/ocrmypdf/builtin_plugins/ghostscript.py b/src/ocrmypdf/builtin_plugins/ghostscript.py
new file mode 100644
index 00000000..92f39008
--- /dev/null
+++ b/src/ocrmypdf/builtin_plugins/ghostscript.py
@@ -0,0 +1,90 @@
+# © 2020 James R. Barlow: github.com/jbarlow83
+#
+# This file is part of OCRmyPDF.
+#
+# OCRmyPDF is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# OCRmyPDF is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with OCRmyPDF. If not, see .
+
+import logging
+from pathlib import Path
+
+from ocrmypdf import hookimpl
+from ocrmypdf._validation import HOCR_OK_LANGS
+from ocrmypdf.exceptions import MissingDependencyError
+from ocrmypdf.exec import check_external_program, ghostscript
+from ocrmypdf.helpers import Resolution
+
+log = logging.getLogger(__name__)
+
+
+@hookimpl
+def check_options(options):
+ gs_version = ghostscript.version()
+ check_external_program(
+ program='gs',
+ package='ghostscript',
+ version_checker=gs_version,
+ need_version='9.15', # limited by Travis CI / Ubuntu 14.04 backports
+ )
+ if gs_version in ('9.24', '9.51'):
+ raise MissingDependencyError(
+ f"Ghostscript {gs_version} contains serious regressions and is not "
+ "supported. Please upgrade to a newer version, or downgrade to the "
+ "previous version."
+ )
+
+ # We have these constraints to check for.
+ # 1. Ghostscript < 9.20 mangles multibyte Unicode
+ # 2. hocr doesn't work on non-Latin languages (so don't select it)
+ is_latin = options.languages.issubset(HOCR_OK_LANGS)
+ if gs_version < '9.20' and options.output_type != 'pdf' and not is_latin:
+ # https://bugs.ghostscript.com/show_bug.cgi?id=696874
+ # Ghostscript < 9.20 fails to encode multibyte characters properly
+ msg = (
+ "The installed version of Ghostscript does not work correctly "
+ "with the OCR languages you specified. Use --output-type pdf or "
+ "upgrade to Ghostscript 9.20 or later to avoid this issue."
+ )
+ msg += f"Found Ghostscript {gs_version}"
+ log.warning(msg)
+
+ if options.output_type == 'pdfa':
+ options.output_type = 'pdfa-2'
+
+ if options.output_type == 'pdfa-3' and ghostscript.version() < '9.19':
+ raise MissingDependencyError(
+ "--output-type pdfa-3 requires Ghostscript 9.19 or later"
+ )
+
+
+@hookimpl
+def rasterize_pdf_page(
+ input_file: Path,
+ output_file: Path,
+ raster_device: str,
+ raster_dpi: Resolution,
+ pageno: int,
+ page_dpi: Resolution = None,
+ rotation: int = None,
+ filter_vector: bool = False,
+):
+ return ghostscript.rasterize_pdf(
+ input_file,
+ output_file,
+ raster_device=raster_device,
+ raster_dpi=raster_dpi,
+ pageno=pageno,
+ page_dpi=page_dpi,
+ rotation=rotation,
+ filter_vector=filter_vector,
+ )
diff --git a/src/ocrmypdf/exec/_support.py b/src/ocrmypdf/exec/_support.py
index 60d22a83..5f52ac87 100644
--- a/src/ocrmypdf/exec/_support.py
+++ b/src/ocrmypdf/exec/_support.py
@@ -280,7 +280,10 @@ def check_external_program(
recommended=False,
):
try:
- found_version = version_checker()
+ if callable(version_checker):
+ found_version = version_checker()
+ else:
+ found_version = version_checker
except (CalledProcessError, FileNotFoundError, MissingDependencyError):
_error_missing_program(program, package, required_for, recommended)
if not recommended:
diff --git a/src/ocrmypdf/exec/ghostscript.py b/src/ocrmypdf/exec/ghostscript.py
index d7407726..a43b972b 100644
--- a/src/ocrmypdf/exec/ghostscript.py
+++ b/src/ocrmypdf/exec/ghostscript.py
@@ -20,7 +20,6 @@
import logging
import os
import re
-import warnings
from io import BytesIO
from os import fspath
from pathlib import Path
@@ -92,22 +91,7 @@ def rasterize_pdf(
rotation: int = None,
filter_vector: bool = False,
):
- """Rasterize one page of a PDF at resolution raster_dpi in canvas units.
-
- The image is sized to match the integer pixels dimensions implied by
- raster_dpi even if those numbers are noninteger. The image's DPI will
- be overridden with the values in page_dpi.
-
- :param input_file: pathlike
- :param output_file: pathlike
- :param raster_device:
- :param raster_dpi: resolution at which to rasterize page
- :param pageno: page number to rasterize (beginning at page 1)
- :param page_dpi: resolution tuple (x, y) overriding output image DPI
- :param rotation: 0, 90, 180, 270: clockwise angle to rotate page
- :param filter_vector: if True, remove vector graphics objects
- :return:
- """
+ """Rasterize one page of a PDF at resolution raster_dpi in canvas units."""
raster_dpi = raster_dpi.round(6)
if not page_dpi:
page_dpi = raster_dpi
diff --git a/src/ocrmypdf/pluginspec.py b/src/ocrmypdf/pluginspec.py
index dd659458..fd2498b2 100644
--- a/src/ocrmypdf/pluginspec.py
+++ b/src/ocrmypdf/pluginspec.py
@@ -24,6 +24,8 @@ from typing import AbstractSet, Optional
import pluggy
from PIL import Image
+from ocrmypdf.helpers import Resolution
+
hookspec = pluggy.HookspecMarker('ocrmypdf')
# pylint: disable=unused-argument
@@ -66,6 +68,35 @@ def validate(pdfinfo: 'PdfInfo', options: Namespace) -> None:
"""
+@hookspec(firstresult=True)
+def rasterize_pdf_page(
+ input_file: Path,
+ output_file: Path,
+ raster_device: str,
+ raster_dpi: Resolution,
+ pageno: int,
+ page_dpi: Resolution = None,
+ rotation: int = None,
+ filter_vector: bool = False,
+) -> None:
+ """Rasterize one page of a PDF at resolution raster_dpi in canvas units.
+
+ The image is sized to match the integer pixels dimensions implied by
+ raster_dpi even if those numbers are noninteger. The image's DPI will
+ be overridden with the values in page_dpi.
+
+ Args:
+ raster_device: type of image to produce at output_file
+ raster_dpi: resolution at which to rasterize page
+ pageno: page number to rasterize (beginning at page 1)
+ page_dpi: resolution, overriding output image DPI
+ rotation: cardinal angle, clockwise, to rotate page
+ filter_vector: if True, remove vector graphics objects
+ Returns:
+ None
+ """
+
+
@hookspec(firstresult=True)
def filter_ocr_image(page: 'PageContext', image: Image) -> Image:
"""Called to filter the image before it is sent to OCR.
diff --git a/tests/test_main.py b/tests/test_main.py
index 6f9d7b50..0b913a14 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -39,7 +39,6 @@ from ocrmypdf.pdfinfo import Colorspace, Encoding, PdfInfo
check_ocrmypdf = pytest.helpers.check_ocrmypdf
run_ocrmypdf = pytest.helpers.run_ocrmypdf
run_ocrmypdf_api = pytest.helpers.run_ocrmypdf_api
-spoof = pytest.helpers.spoof
RENDERERS = ['hocr', 'sandwich']
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index 59250eac..16175140 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -47,7 +47,6 @@ pytestmark = pytest.mark.filterwarnings('ignore:.*XMLParser.*:DeprecationWarning
check_ocrmypdf = pytest.helpers.check_ocrmypdf
run_ocrmypdf = pytest.helpers.run_ocrmypdf
-spoof = pytest.helpers.spoof
@pytest.mark.parametrize("output_type", ['pdfa', 'pdf'])
diff --git a/tests/test_validation.py b/tests/test_validation.py
index f6fcc775..3e6272af 100644
--- a/tests/test_validation.py
+++ b/tests/test_validation.py
@@ -29,19 +29,29 @@ from ocrmypdf.exceptions import BadArgsError, MissingDependencyError
from ocrmypdf.pdfinfo import PdfInfo
-def make_opts(input_file='a.pdf', output_file='b.pdf', language='eng', **kwargs):
+def make_opts_pm(input_file='a.pdf', output_file='b.pdf', language='eng', **kwargs):
if language is not None:
kwargs['language'] = language
parser = get_parser()
pm = get_plugin_manager(kwargs.get('plugins', []))
pm.hook.add_options(parser=parser)
- return create_options(
- input_file=input_file, output_file=output_file, parser=parser, **kwargs
+ return (
+ create_options(
+ input_file=input_file, output_file=output_file, parser=parser, **kwargs
+ ),
+ pm,
)
+def make_opts(*args, **kwargs):
+ opts, _pm = make_opts_pm(*args, **kwargs)
+ return opts
+
+
def test_hocr_notlatin_warning(caplog):
- vd.check_options_output(make_opts(language='chi_sim', pdf_renderer='hocr'))
+ vd.check_options(
+ *make_opts_pm(language='chi_sim', pdf_renderer='hocr', output_type='pdfa')
+ )
assert 'PDF renderer is known to cause' in caplog.text
@@ -49,20 +59,20 @@ def test_old_ghostscript(caplog):
with patch('ocrmypdf.exec.ghostscript.version', return_value='9.19'), patch(
'ocrmypdf.exec.tesseract.has_textonly_pdf', return_value=True
):
- vd.check_options_output(make_opts(language='chi_sim', output_type='pdfa'))
+ vd.check_options(*make_opts_pm(language='chi_sim', output_type='pdfa'))
assert 'Ghostscript does not work correctly' in caplog.text
with patch('ocrmypdf.exec.ghostscript.version', return_value='9.18'), patch(
'ocrmypdf.exec.tesseract.has_textonly_pdf', return_value=True
):
with pytest.raises(MissingDependencyError):
- vd.check_options_output(make_opts(output_type='pdfa-3'))
+ vd.check_options(*make_opts_pm(output_type='pdfa-3'))
with patch('ocrmypdf.exec.ghostscript.version', return_value='9.24'), patch(
'ocrmypdf.exec.tesseract.has_textonly_pdf', return_value=True
):
with pytest.raises(MissingDependencyError):
- vd.check_dependency_versions(make_opts())
+ vd.check_options(*make_opts_pm())
def test_old_tesseract_error():
@@ -97,7 +107,6 @@ def test_optimizing(caplog):
def test_user_words(caplog):
-
with patch('ocrmypdf.exec.tesseract.has_user_words', return_value=False):
opts = make_opts(user_words='foo')
plugin_manager = get_plugin_manager(opts.plugins)