From 61069660a28f0521e50ad1a439d7a9abae8f02a7 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Sun, 12 Jun 2022 02:42:16 -0700 Subject: [PATCH] Move optimization options to plugin --- src/ocrmypdf/_pipeline.py | 2 +- src/ocrmypdf/_validation.py | 34 +------ src/ocrmypdf/builtin_plugins/optimize.py | 108 +++++++++++++++++++++++ src/ocrmypdf/cli.py | 66 -------------- tests/test_validation.py | 6 +- 5 files changed, 113 insertions(+), 103 deletions(-) diff --git a/src/ocrmypdf/_pipeline.py b/src/ocrmypdf/_pipeline.py index 244c487f..13cfdc06 100644 --- a/src/ocrmypdf/_pipeline.py +++ b/src/ocrmypdf/_pipeline.py @@ -822,7 +822,7 @@ def metadata_fixup(working_file: Path, context: PdfContext): **get_pdf_save_settings(options.output_type), linearize=( # Don't linearize if optimize() will be linearizing too should_linearize(working_file, context) - if options.optimize == 0 + if hasattr(options, 'optimize') and options.optimize == 0 else False ), ) diff --git a/src/ocrmypdf/_validation.py b/src/ocrmypdf/_validation.py index 3ddda46c..275dad9a 100644 --- a/src/ocrmypdf/_validation.py +++ b/src/ocrmypdf/_validation.py @@ -194,37 +194,6 @@ def check_options_ocr_behavior(options): options.pages = _pages_from_ranges(options.pages) -def check_options_optimizing(options): - if options.optimize >= 2: - check_external_program( - program='pngquant', - package='pngquant', - version_checker=pngquant.version, - need_version='2.0.1', - required_for='--optimize {2,3}', - ) - - if options.optimize >= 2: - # Although we use JBIG2 for optimize=1, don't nag about it unless the - # user is asking for more optimization - check_external_program( - program='jbig2', - package='jbig2enc', - version_checker=jbig2enc.version, - need_version='0.28', - required_for='--optimize {2,3} | --jbig2-lossy', - recommended=True if not options.jbig2_lossy else False, - ) - - if options.optimize == 0 and any( - [options.jbig2_lossy, options.png_quality, options.jpeg_quality] - ): - log.warning( - "The arguments --jbig2-lossy, --png-quality, and --jpeg-quality " - "will be ignored because --optimize=0." - ) - - def check_options_advanced(options): if options.pdfa_image_compression != 'auto' and not options.output_type.startswith( 'pdfa' @@ -262,7 +231,6 @@ def _check_options(options, plugin_manager, ocr_engine_languages): check_options_sidecar(options) check_options_preprocessing(options) check_options_ocr_behavior(options) - check_options_optimizing(options) check_options_advanced(options) check_options_pillow(options) plugin_manager.hook.check_options(options=options) @@ -356,7 +324,7 @@ def report_output_file_size( f"The argument --{arg.replace('_', '-')} was issued, causing transcoding." ) - if options.optimize == 0: + if hasattr(options, 'optimize') and options.optimize == 0: reasons.append("Optimization was disabled.") else: image_optimizers = { diff --git a/src/ocrmypdf/builtin_plugins/optimize.py b/src/ocrmypdf/builtin_plugins/optimize.py index b01d87b5..30386ccd 100644 --- a/src/ocrmypdf/builtin_plugins/optimize.py +++ b/src/ocrmypdf/builtin_plugins/optimize.py @@ -7,12 +7,120 @@ """Built-in plugin to implement PDF page optimization.""" +import argparse +import logging from pathlib import Path from ocrmypdf import PdfContext, hookimpl from ocrmypdf._concurrent import Executor +from ocrmypdf._exec import jbig2enc, pngquant from ocrmypdf._pipeline import get_pdf_save_settings, should_linearize +from ocrmypdf.cli import numeric from ocrmypdf.optimize import optimize +from ocrmypdf.subprocess import check_external_program + +log = logging.getLogger(__name__) + + +@hookimpl +def add_options(parser): + optimizing = parser.add_argument_group( + "Optimization options", "Control how the PDF is optimized after OCR" + ) + optimizing.add_argument( + '-O', + '--optimize', + type=int, + choices=range(0, 4), + default=1, + help=( + "Control how PDF is optimized after processing:" + "0 - do not optimize; " + "1 - do safe, lossless optimizations (default); " + "2 - do lossy JPEG and JPEG2000 optimizations; " + "3 - do more aggressive lossy JPEG and JPEG2000 optimizations. " + "To enable lossy JBIG2, see --jbig2-lossy." + ), + ) + optimizing.add_argument( + '--jpeg-quality', + type=numeric(int, 0, 100), + default=0, + metavar='Q', + help=( + "Adjust JPEG quality level for JPEG optimization. " + "100 is best quality and largest output size; " + "1 is lowest quality and smallest output; " + "0 uses the default." + ), + ) + optimizing.add_argument( + '--jpg-quality', + type=numeric(int, 0, 100), + default=0, + metavar='Q', + dest='jpeg_quality', + help=argparse.SUPPRESS, # Alias for --jpeg-quality + ) + optimizing.add_argument( + '--png-quality', + type=numeric(int, 0, 100), + default=0, + metavar='Q', + help=( + "Adjust PNG quality level to use when quantizing PNGs. " + "Values have same meaning as with --jpeg-quality" + ), + ) + optimizing.add_argument( + '--jbig2-lossy', + action='store_true', + help=( + "Enable JBIG2 lossy mode (better compression, not suitable for some " + "use cases - see documentation). Only takes effect if --optimize 1 or " + "higher is also enabled." + ), + ) + optimizing.add_argument( + '--jbig2-page-group-size', + type=numeric(int, 1, 10000), + default=0, + metavar='N', + # Adjust number of pages to consider at once for JBIG2 compression + help=argparse.SUPPRESS, + ) + + +@hookimpl +def check_options(options): + if options.optimize >= 2: + check_external_program( + program='pngquant', + package='pngquant', + version_checker=pngquant.version, + need_version='2.0.1', + required_for='--optimize {2,3}', + ) + + if options.optimize >= 2: + # Although we use JBIG2 for optimize=1, don't nag about it unless the + # user is asking for more optimization + check_external_program( + program='jbig2', + package='jbig2enc', + version_checker=jbig2enc.version, + need_version='0.28', + required_for='--optimize {2,3} | --jbig2-lossy', + recommended=True if not options.jbig2_lossy else False, + ) + + if options.optimize == 0 and any( + [options.jbig2_lossy, options.png_quality, options.jpeg_quality] + ): + log.warning( + "The arguments --jbig2-lossy, --png-quality, and --jpeg-quality " + "will be ignored because --optimize=0." + ) @hookimpl diff --git a/src/ocrmypdf/cli.py b/src/ocrmypdf/cli.py index 4a99eaf0..079c531b 100644 --- a/src/ocrmypdf/cli.py +++ b/src/ocrmypdf/cli.py @@ -352,72 +352,6 @@ Online documentation is located at: "but include skipped pages in final output", ) - optimizing = parser.add_argument_group( - "Optimization options", "Control how the PDF is optimized after OCR" - ) - optimizing.add_argument( - '-O', - '--optimize', - type=int, - choices=range(0, 4), - default=1, - help=( - "Control how PDF is optimized after processing:" - "0 - do not optimize; " - "1 - do safe, lossless optimizations (default); " - "2 - do lossy JPEG and JPEG2000 optimizations; " - "3 - do more aggressive lossy JPEG and JPEG2000 optimizations. " - "To enable lossy JBIG2, see --jbig2-lossy." - ), - ) - optimizing.add_argument( - '--jpeg-quality', - type=numeric(int, 0, 100), - default=0, - metavar='Q', - help=( - "Adjust JPEG quality level for JPEG optimization. " - "100 is best quality and largest output size; " - "1 is lowest quality and smallest output; " - "0 uses the default." - ), - ) - optimizing.add_argument( - '--jpg-quality', - type=numeric(int, 0, 100), - default=0, - metavar='Q', - dest='jpeg_quality', - help=argparse.SUPPRESS, # Alias for --jpeg-quality - ) - optimizing.add_argument( - '--png-quality', - type=numeric(int, 0, 100), - default=0, - metavar='Q', - help=( - "Adjust PNG quality level to use when quantizing PNGs. " - "Values have same meaning as with --jpeg-quality" - ), - ) - optimizing.add_argument( - '--jbig2-lossy', - action='store_true', - help=( - "Enable JBIG2 lossy mode (better compression, not suitable for some " - "use cases - see documentation). Only takes effect if --optimize 1 or " - "higher is also enabled." - ), - ) - optimizing.add_argument( - '--jbig2-page-group-size', - type=numeric(int, 1, 10000), - default=0, - metavar='N', - # Adjust number of pages to consider at once for JBIG2 compression - help=argparse.SUPPRESS, - ) - advanced = parser.add_argument_group( "Advanced", "Advanced options to control OCRmyPDF" ) diff --git a/tests/test_validation.py b/tests/test_validation.py index 280e25f8..3c704f00 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -92,9 +92,9 @@ def test_mutex_options(): def test_optimizing(caplog): - vd.check_options_optimizing( - make_opts(optimize=0, jbig2_lossy=True, png_quality=18, jpeg_quality=10) - ) + opts = make_opts(optimize=0, jbig2_lossy=True, png_quality=18, jpeg_quality=10) + plugin_manager = get_plugin_manager(opts.plugins) + vd._check_options(opts, plugin_manager, set()) assert 'will be ignored because' in caplog.text