diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 518cef17..38229de0 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -5,10 +5,11 @@ OCRmyPDF uses `semantic versioning `_ for its command line i The OCRmyPDF package itself does not contain a public API, although it is fairly stable and breaking changes are usually timed with a major release. A future release will clearly define the stable public API. -v5.3.4 +v5.4 ------ - Change wording of a deprecation warning to improve clarity +- Added option to generate PDF/A-1b output if desired (``--output-type pdfa-1``); default remains PDF/A-2b generation - Update documentation diff --git a/ocrmypdf/__main__.py b/ocrmypdf/__main__.py index 4d671ee5..9304c48e 100755 --- a/ocrmypdf/__main__.py +++ b/ocrmypdf/__main__.py @@ -130,12 +130,15 @@ parser.add_argument( '--image-dpi', metavar='DPI', type=int, help="For input image instead of PDF, use this DPI instead of file's.") parser.add_argument( - '--output-type', choices=['pdfa', 'pdf'], default='pdfa', + '--output-type', choices=['pdfa', 'pdf', 'pdfa-1', 'pdfa-2'], + default='pdfa', help="Choose output type. 'pdfa' creates a PDF/A-2b compliant file for " "long term archiving (default, recommended) but may not suitable " "for users who want their file altered as little as possible. 'pdfa' " "also has problems with full Unicode text. 'pdf' attempts to " - "preserve file contents as much as possible.") + "preserve file contents as much as possible. 'pdf-a1' creates a " + "PDF/A1-b file. 'pdf-a2' is equivalent to 'pdfa'." + ) # Use null string '\0' as sentinel to indicate the user supplied no argument, # since that is the only invalid character for filepaths on all platforms @@ -759,7 +762,7 @@ def run_pipeline(): if options.flowchart: _log.info("Flowchart saved to {}".format(options.flowchart)) elif options.output_file != '-': - if options.output_type == 'pdfa': + if options.output_type.startswith('pdfa'): pdfa_info = file_claims_pdfa(options.output_file) if pdfa_info['pass']: msg = 'Output file is a {} (as expected)' @@ -767,7 +770,6 @@ def run_pipeline(): else: msg = 'Output file is okay but is not PDF/A (seems to be {})' _log.warning(msg.format(pdfa_info['conformance'])) - return ExitCode.invalid_output_pdf if not qpdf.check(options.output_file, _log): _log.warning('Output file: The generated PDF is INVALID') diff --git a/ocrmypdf/exec/ghostscript.py b/ocrmypdf/exec/ghostscript.py index 4190f9c4..5035abc9 100644 --- a/ocrmypdf/exec/ghostscript.py +++ b/ocrmypdf/exec/ghostscript.py @@ -104,7 +104,7 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log, def generate_pdfa(pdf_pages, output_file, compression, log, - threads=1, pdf_version='1.5'): + threads=1, pdf_version='1.5', pdfa_part='2'): compression_args = [] if compression == 'jpeg': compression_args = [ @@ -140,7 +140,7 @@ def generate_pdfa(pdf_pages, output_file, compression, log, "-sProcessColorModel=DeviceRGB" ] + compression_args + [ "-dJPEGQ=95", - "-dPDFA=2", + "-dPDFA=" + pdfa_part, "-dPDFACompatibilityPolicy=1", "-sOutputFile=" + gs_pdf.name, ] diff --git a/ocrmypdf/pipeline.py b/ocrmypdf/pipeline.py index 487640c2..e23c085b 100644 --- a/ocrmypdf/pipeline.py +++ b/ocrmypdf/pipeline.py @@ -879,7 +879,8 @@ def merge_pages_ghostscript( output_file=output_file, compression=options.pdfa_image_compression, log=log, - threads=options.jobs or 1) + threads=options.jobs or 1, + pdfa_part=('1' if options.output_type == 'pdfa-1' else '2')) def merge_pages_qpdf( @@ -1164,7 +1165,7 @@ def build_pipeline(options, work_folder, log, context): filter=formatter(r'\.repaired\.pdf'), output=os.path.join(work_folder, 'pdfa.ps'), extras=[log, context]) - task_generate_postscript_stub.active_if(options.output_type == 'pdfa') + task_generate_postscript_stub.active_if(options.output_type.startswith('pdfa')) # Bypass valve @@ -1186,7 +1187,7 @@ def build_pipeline(options, work_folder, log, context): task_generate_postscript_stub], output=os.path.join(work_folder, 'merged.pdf'), extras=[log, context]) - task_merge_pages_ghostscript.active_if(options.output_type == 'pdfa') + task_merge_pages_ghostscript.active_if(options.output_type.startswith('pdfa')) task_merge_pages_qpdf = main_pipeline.merge( task_func=merge_pages_qpdf, diff --git a/tests/test_main.py b/tests/test_main.py index f14bad20..523f1387 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -982,3 +982,14 @@ def test_sidecar_pagecount(spoof_tesseract_cache, resources, outpdf): # formfeeds is the page count less one assert ocr_text.count('\f') == num_pages - 1, \ "Sidecar page count does not match PDF page count" + + +def test_pdfa_1(spoof_tesseract_cache, resources, outpdf): + check_ocrmypdf( + resources / 'ccitt.pdf', outpdf, + '--output-type', 'pdfa-1', + env=spoof_tesseract_cache + ) + + pdfa_info = file_claims_pdfa(outpdf) + assert pdfa_info['conformance'] == 'PDF/A-1B' \ No newline at end of file