diff --git a/src/ocrmypdf/_pipeline.py b/src/ocrmypdf/_pipeline.py index 6e67d808..094b0858 100644 --- a/src/ocrmypdf/_pipeline.py +++ b/src/ocrmypdf/_pipeline.py @@ -688,9 +688,10 @@ def convert_to_pdfa(input_pdf, input_ps_stub, context): else: safe_symlink(input_pdf, fix_docinfo_file) - ghostscript.generate_pdfa( + context.plugin_manager.hook.generate_pdfa( pdf_version=input_pdfinfo.min_version, - pdf_pages=[fix_docinfo_file, input_ps_stub], + pdf_pages=[fix_docinfo_file], + pdfmark=input_ps_stub, output_file=output_file, compression=options.pdfa_image_compression, pdfa_part=options.output_type[-1], # is pdfa-1, pdfa-2, or pdfa-3 diff --git a/src/ocrmypdf/builtin_plugins/ghostscript.py b/src/ocrmypdf/builtin_plugins/ghostscript.py index 92f39008..90cf41a3 100644 --- a/src/ocrmypdf/builtin_plugins/ghostscript.py +++ b/src/ocrmypdf/builtin_plugins/ghostscript.py @@ -69,14 +69,14 @@ def check_options(options): @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, + input_file, + output_file, + raster_device, + raster_dpi, + pageno, + page_dpi=None, + rotation=None, + filter_vector=False, ): return ghostscript.rasterize_pdf( input_file, @@ -88,3 +88,14 @@ def rasterize_pdf_page( rotation=rotation, filter_vector=filter_vector, ) + + +@hookimpl +def generate_pdfa(pdf_pages, pdfmark, output_file, compression, pdf_version, pdfa_part): + return ghostscript.generate_pdfa( + pdf_pages=[*pdf_pages, pdfmark], + output_file=output_file, + compression=compression, + pdf_version=pdf_version, + pdfa_part=pdfa_part, + ) diff --git a/src/ocrmypdf/exec/ghostscript.py b/src/ocrmypdf/exec/ghostscript.py index a43b972b..9fe690b9 100644 --- a/src/ocrmypdf/exec/ghostscript.py +++ b/src/ocrmypdf/exec/ghostscript.py @@ -154,24 +154,6 @@ def generate_pdfa( pdf_version: str = '1.5', pdfa_part: str = '2', ): - """Generate a PDF/A. - - The pdf_pages, a list files, will be merged into output_file. One or more - PDF files may be merged. One of the files in this list must be a pdfmark - file that provides Ghostscript with details on how to perform the PDF/A - conversion. By default with we pick PDF/A-2b, but this works for 1 or 3. - - compression can be 'jpeg', 'lossless', or an empty string. In 'jpeg', - Ghostscript is instructed to convert color and grayscale images to DCT - (JPEG encoding). In 'lossless' Ghostscript is told to convert images to - Flate (lossless/PNG). If the parameter is omitted Ghostscript is left to - make its own decisions about how to encode images; it appears to use a - heuristic to decide how to encode images. As of Ghostscript 9.25, we - support passthrough JPEG which allows Ghostscript to avoid transcoding - images entirely. (The feature was added in 9.23 but broken, and the 9.24 - release of Ghostscript had regressions, so we don't support it until 9.25.) - """ - compression_args = [] if compression == 'jpeg': compression_args = [ diff --git a/src/ocrmypdf/pluginspec.py b/src/ocrmypdf/pluginspec.py index fd2498b2..b83aaaa2 100644 --- a/src/ocrmypdf/pluginspec.py +++ b/src/ocrmypdf/pluginspec.py @@ -19,7 +19,7 @@ from abc import ABC, abstractstaticmethod from argparse import ArgumentParser, Namespace from collections import namedtuple from pathlib import Path -from typing import AbstractSet, Optional +from typing import AbstractSet, List, Optional import pluggy from PIL import Image @@ -75,8 +75,8 @@ def rasterize_pdf_page( raster_device: str, raster_dpi: Resolution, pageno: int, - page_dpi: Resolution = None, - rotation: int = None, + page_dpi: Optional[Resolution] = None, + rotation: Optional[int] = None, filter_vector: bool = False, ) -> None: """Rasterize one page of a PDF at resolution raster_dpi in canvas units. @@ -162,3 +162,31 @@ class OcrEngine(ABC): @hookspec(firstresult=True) def get_ocr_engine() -> OcrEngine: pass + + +@hookspec(firstresult=True) +def generate_pdfa( + pdf_pages: List[Path], + pdfmark: Path, + output_file: Path, + compression: str, + pdf_version: str, + pdfa_part: str, +): + """Generate a PDF/A. + + The pdf_pages, a list of files, will be merged into output_file. One or more + PDF files may be merged. The pdfmark file is a PostScript.ps file that + provides Ghostscript with details on how to perform the PDF/A + conversion. By default with we pick PDF/A-2b, but this works for 1 or 3. + + compression can be 'jpeg', 'lossless', or an empty string. In 'jpeg', + Ghostscript is instructed to convert color and grayscale images to DCT + (JPEG encoding). In 'lossless' Ghostscript is told to convert images to + Flate (lossless/PNG). If the parameter is omitted Ghostscript is left to + make its own decisions about how to encode images; it appears to use a + heuristic to decide how to encode images. As of Ghostscript 9.25, we + support passthrough JPEG which allows Ghostscript to avoid transcoding + images entirely. (The feature was added in 9.23 but broken, and the 9.24 + release of Ghostscript had regressions, so we don't support it until 9.25.) + """ diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 16175140..1d310107 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -341,7 +341,9 @@ def test_prevent_gs_invalid_xml(resources, outdir): args=['-j', '1', '--output-type', 'pdfa-2', 'a.pdf', 'b.pdf'] ) pdfinfo = PdfInfo(outdir / 'layers.rendered.pdf') - context = PdfContext(options, outdir, outdir / 'layers.rendered.pdf', pdfinfo, None) + context = PdfContext( + options, outdir, outdir / 'layers.rendered.pdf', pdfinfo, get_plugin_manager([]) + ) convert_to_pdfa( str(outdir / 'layers.rendered.pdf'), str(outdir / 'pdfa.ps'), context @@ -372,7 +374,9 @@ def test_malformed_docinfo(caplog, resources, outdir): args=['-j', '1', '--output-type', 'pdfa-2', 'a.pdf', 'b.pdf'] ) pdfinfo = PdfInfo(outdir / 'layers.rendered.pdf') - context = PdfContext(options, outdir, outdir / 'layers.rendered.pdf', pdfinfo, None) + context = PdfContext( + options, outdir, outdir / 'layers.rendered.pdf', pdfinfo, get_plugin_manager([]) + ) convert_to_pdfa( str(outdir / 'layers.rendered.pdf'), str(outdir / 'pdfa.ps'), context diff --git a/tests/test_preprocessing.py b/tests/test_preprocessing.py index e24c3b7d..865b5e58 100644 --- a/tests/test_preprocessing.py +++ b/tests/test_preprocessing.py @@ -31,7 +31,6 @@ from ocrmypdf.pdfinfo import 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_stdio.py b/tests/test_stdio.py index 2f58306a..883d48de 100644 --- a/tests/test_stdio.py +++ b/tests/test_stdio.py @@ -30,7 +30,6 @@ from ocrmypdf.helpers import check_pdf run_ocrmypdf = pytest.helpers.run_ocrmypdf run_ocrmypdf_api = pytest.helpers.run_ocrmypdf -spoof = pytest.helpers.spoof def test_stdin(ocrmypdf_exec, resources, outpdf):