Move Ghostscript rasterize_pdf to plugin
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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."
|
||||
)
|
||||
|
||||
@@ -14,5 +14,3 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ocrmypdf.builtin_plugins.tesseract_ocr import *
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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,
|
||||
)
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user