From 8d848284dfa2b4ecaf702294f8a6ae1135040d35 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Mon, 10 Aug 2015 16:05:00 -0700 Subject: [PATCH] Fix code, test case: complain when GS fails to produce PDF/A Modified pipeline to fix regression and return the proper error code if we did not produce a PDF/A as expected. The wrapper forces the output to be PDF 1.3 which is not PDF/A compliant. The funny thing is that in some cases JHOVE incorrectly states that a file is PDF/A-1b compliant, well formed and valid, even when it is not according to Acrobat XI and is missing the PDF/A metadata marker, as far as I can tell. JHOVE may not be as beneficial as hoped. --- ocrmypdf/main.py | 35 +++++++++++++++++++++-------- tests/replace_ghostscript_nopdfa.py | 35 ++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/ocrmypdf/main.py b/ocrmypdf/main.py index bd1cebd5..77ce047e 100755 --- a/ocrmypdf/main.py +++ b/ocrmypdf/main.py @@ -758,12 +758,18 @@ def merge_pages( filter=formatter(), output=options.output_file, extras=[_log, _pdfinfo, _pdfinfo_lock]) -def validate_pdfa( +def copy_final( input_file, output_file, log, pdfinfo, pdfinfo_lock): + shutil.copy(input_file, output_file) + + +def validate_pdfa( + input_file, + log): args_jhove = [ 'java', @@ -798,13 +804,7 @@ def validate_pdfa( re.IGNORECASE | re.MULTILINE): pdf_is_pdfa = True - if not pdf_is_valid: - log.warning('Output file: The generated PDF/A file is INVALID') - elif pdf_is_valid and not pdf_is_pdfa: - log.warning('Output file: Generated file is a VALID PDF but not PDF/A') - elif pdf_is_valid and pdf_is_pdfa: - log.info('Output file: The generated PDF/A file is VALID') - shutil.copy(input_file, output_file) + return (pdf_is_valid, pdf_is_pdfa) # @active_if(ocr_required and options.exact_image) @@ -845,8 +845,25 @@ def available_cpu_count(): def run_pipeline(): if not options.jobs or options.jobs == 1: options.jobs = available_cpu_count() + cmdline.run(options) + pdf_is_valid, pdf_is_pdfa = validate_pdfa(options.output_file, _log) + + returncode = EXIT_OTHER_ERROR # Assume error + + if not pdf_is_valid: + _log.warning('Output file: The generated PDF/A file is INVALID') + returncode = EXIT_INVALID_OUTPUT_PDFA + elif pdf_is_valid and not pdf_is_pdfa: + _log.warning('Output file: Generated file is VALID PDF but not PDF/A') + returncode = EXIT_INVALID_OUTPUT_PDFA + elif pdf_is_valid and pdf_is_pdfa: + _log.info('Output file: The generated PDF/A file is VALID') + returncode = 0 + + return returncode + if __name__ == '__main__': - run_pipeline() + sys.exit(run_pipeline()) diff --git a/tests/replace_ghostscript_nopdfa.py b/tests/replace_ghostscript_nopdfa.py index d07682a3..918c57f5 100755 --- a/tests/replace_ghostscript_nopdfa.py +++ b/tests/replace_ghostscript_nopdfa.py @@ -10,7 +10,15 @@ created instead. It assumes that it is called from a staged system PATH where the first item on the PATH contains a file named 'gs' which is a symlink to this file. It will strip out the first item on path to invoke the real -'gs'. +'gs'. That is, it expects this when called + +1. tests/output/[...test name...]/bin/gs is a symlink to this file +2. tests/output/bin is the first item on PATH +3. The real executable is on the path + +OCRmyPDF also calls "gs --version" and "gs --help". gs answers both +on stdout so, this wrapper prints to stderr. + ''' @@ -21,15 +29,36 @@ def pdfa_param(arg): return True if arg.endswith('.ps'): return True + if 'ColorConversionStrategy' in arg: + return True + if 'ProcessColorModel' in arg: + return True + if 'OutputICCProfile' in arg: + return True return False if __name__ == '__main__': - args = [arg for arg in sys.argv[1:] - if not pdfa_param(arg)] + sys_args = sys.argv[1:] print("Fake Ghostscript wrapper", file=sys.stderr) + if any(pdfa_param(arg) for arg in sys_args): + # We were asked to produce a PDF/A + # Filter out PDF/A arguments + args = [arg for arg in sys.argv[1:] + if not pdfa_param(arg)] + + # Tell Ghostscript to create a PDF 1.3 instead so JHOVE won't + # think it's a PDF/A + indexof_pdfwrite = next(n for n, item in enumerate(args) + if 'pdfwrite' in item) + args.insert(indexof_pdfwrite + 1, '-dCompatibilityLevel=1.3') + print("Rewrote arguments", file=sys.stderr) + else: + args = sys_args + print("Keeping arguments", file=sys.stderr) + exec_path = os.environ['PATH'].split(os.pathsep) env = os.environ.copy() env['PATH'] = os.pathsep.join(exec_path[1:])