From 7e1223c12c295152ddb245e8e51a870f1f5659f0 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Sun, 29 Nov 2020 14:53:35 -0800 Subject: [PATCH] ghostscript: add output tracing --- src/ocrmypdf/_exec/ghostscript.py | 43 +++++++++++++++++++++++++----- tests/plugins/gs_pdfa_failure.py | 6 ++--- tests/plugins/gs_render_failure.py | 2 +- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/ocrmypdf/_exec/ghostscript.py b/src/ocrmypdf/_exec/ghostscript.py index 0644e0d3..bbf13277 100644 --- a/src/ocrmypdf/_exec/ghostscript.py +++ b/src/ocrmypdf/_exec/ghostscript.py @@ -18,10 +18,11 @@ from subprocess import PIPE, CalledProcessError from typing import Optional, cast from PIL import Image +from tqdm import tqdm from ocrmypdf.exceptions import MissingDependencyError, SubprocessOutputError from ocrmypdf.helpers import Resolution -from ocrmypdf.subprocess import get_version, run +from ocrmypdf.subprocess import get_version, run, run_polling_stderr log = logging.getLogger(__name__) @@ -139,6 +140,27 @@ def rasterize_pdf( im.save(fspath(output_file), dpi=page_dpi) +class GhostscriptFollower: + re_process = re.compile(r"Processing pages \d+ through (\d+).") + re_page = re.compile(r"Page (\d+)") + + def __init__(self): + self.count = 0 + self.tqdm = None + + def __call__(self, line): + if not self.tqdm: + m = self.re_process.match(line.strip()) + if m: + self.count = int(m.group(1)) + self.tqdm = tqdm(total=self.count, desc="Ghostscript", unit='page') + return + else: + m = self.re_page.match(line.strip()) + if m: + self.tqdm.update() + + def generate_pdfa( pdf_pages, output_file: os.PathLike, @@ -188,7 +210,6 @@ def generate_pdfa( args_gs = ( [ GS, - "-dQUIET", "-dBATCH", "-dNOPAUSE", "-dSAFER", @@ -208,16 +229,26 @@ def generate_pdfa( ] ) args_gs.extend(fspath(s) for s in pdf_pages) # Stringify Path objs + try: with Path(output_file).open('wb') as output: - p = run(args_gs, stdout=output, stderr=PIPE, check=True) + p = run_polling_stderr( + args_gs, + stdout=output, + stderr=PIPE, + check=True, + text=True, + encoding='utf-8', + errors='replace', + callback=GhostscriptFollower(), + ) except CalledProcessError as e: # Ghostscript does not change return code when it fails to create # PDF/A - check PDF/A status elsewhere - log.error(e.stderr.decode(errors='replace')) - raise SubprocessOutputError('Ghostscript PDF/A rendering failed') + log.error(e.stderr) + raise SubprocessOutputError('Ghostscript PDF/A rendering failed') from e else: - stderr = p.stderr.decode('utf-8', errors='replace') + stderr = p.stderr if _gs_error_reported(stderr): last_part = None repcount = 0 diff --git a/tests/plugins/gs_pdfa_failure.py b/tests/plugins/gs_pdfa_failure.py index dcad94f6..8a694de9 100644 --- a/tests/plugins/gs_pdfa_failure.py +++ b/tests/plugins/gs_pdfa_failure.py @@ -23,7 +23,7 @@ from unittest.mock import patch from ocrmypdf import hookimpl from ocrmypdf.builtin_plugins import ghostscript -from ocrmypdf.subprocess import run +from ocrmypdf.subprocess import run_polling_stderr def run_rig_args(args, **kwargs): @@ -33,13 +33,13 @@ def run_rig_args(args, **kwargs): new_args = [ arg for arg in args if not arg.startswith('-dPDFA') and not arg.endswith('.ps') ] - proc = run(new_args, **kwargs) + proc = run_polling_stderr(new_args, **kwargs) return proc @hookimpl def generate_pdfa(pdf_pages, pdfmark, output_file, compression, pdf_version, pdfa_part): - with patch('ocrmypdf._exec.ghostscript.run', new=run_rig_args): + with patch('ocrmypdf._exec.ghostscript.run_polling_stderr', new=run_rig_args): ghostscript.generate_pdfa( pdf_pages=pdf_pages, pdfmark=pdfmark, diff --git a/tests/plugins/gs_render_failure.py b/tests/plugins/gs_render_failure.py index e3cee162..2cad1f4e 100644 --- a/tests/plugins/gs_render_failure.py +++ b/tests/plugins/gs_render_failure.py @@ -34,7 +34,7 @@ def raise_gs_fail(*args, **kwargs): @hookimpl def generate_pdfa(pdf_pages, pdfmark, output_file, compression, pdf_version, pdfa_part): - with patch('ocrmypdf._exec.ghostscript.run', new=raise_gs_fail): + with patch('ocrmypdf._exec.ghostscript.run_polling_stderr', new=raise_gs_fail): ghostscript.generate_pdfa( pdf_pages=pdf_pages, pdfmark=pdfmark,