From 9bccff4f885b4cbf3e38aa36437073c9003997d4 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Sat, 16 May 2020 03:24:31 -0700 Subject: [PATCH] Move Tesseract specific arguments to plugin --- src/ocrmypdf/__main__.py | 10 +--- src/ocrmypdf/_plugin_manager.py | 12 ++++ src/ocrmypdf/_sync.py | 2 +- src/ocrmypdf/builtin_plugins/tesseract_ocr.py | 56 +++++++++++++++++++ src/ocrmypdf/cli.py | 55 +----------------- tests/conftest.py | 20 +++---- tests/test_validation.py | 5 +- 7 files changed, 87 insertions(+), 73 deletions(-) diff --git a/src/ocrmypdf/__main__.py b/src/ocrmypdf/__main__.py index 1cf84daf..69d68db4 100755 --- a/src/ocrmypdf/__main__.py +++ b/src/ocrmypdf/__main__.py @@ -22,7 +22,7 @@ import sys from multiprocessing import set_start_method from ocrmypdf import __version__ -from ocrmypdf._plugin_manager import get_plugin_manager +from ocrmypdf._plugin_manager import get_parser_options_plugins from ocrmypdf._sync import run_pipeline from ocrmypdf._validation import check_closed_streams, check_options from ocrmypdf.api import Verbosity, configure_logging @@ -33,13 +33,7 @@ log = logging.getLogger('ocrmypdf') def run(args=None): - pre_options, _unused = plugins_only_parser.parse_known_args(args=args) - plugin_manager = get_plugin_manager(pre_options.plugins) - - parser = get_parser() - plugin_manager.hook.add_options(parser=parser) - - options = parser.parse_args(args=args) + parser, options, plugin_manager = get_parser_options_plugins(args=args) if not check_closed_streams(options): return ExitCode.bad_args diff --git a/src/ocrmypdf/_plugin_manager.py b/src/ocrmypdf/_plugin_manager.py index e42ef94b..6216a44b 100644 --- a/src/ocrmypdf/_plugin_manager.py +++ b/src/ocrmypdf/_plugin_manager.py @@ -24,6 +24,7 @@ from typing import List import pluggy from ocrmypdf import pluginspec +from ocrmypdf.cli import get_parser, plugins_only_parser def get_plugin_manager(plugins: List[str], builtins=True): @@ -47,3 +48,14 @@ def get_plugin_manager(plugins: List[str], builtins=True): module = importlib.import_module(name) pm.register(module) return pm + + +def get_parser_options_plugins(args): + pre_options, _unused = plugins_only_parser.parse_known_args(args=args) + plugin_manager = get_plugin_manager(pre_options.plugins) + + parser = get_parser() + plugin_manager.hook.add_options(parser=parser) + + options = parser.parse_args(args=args) + return parser, options, plugin_manager diff --git a/src/ocrmypdf/_sync.py b/src/ocrmypdf/_sync.py index 3f97a301..c25bf8e8 100644 --- a/src/ocrmypdf/_sync.py +++ b/src/ocrmypdf/_sync.py @@ -305,7 +305,7 @@ def run_pipeline(options, *, plugin_manager, api=False): if not options.jobs: options.jobs = available_cpu_count() if not plugin_manager: - plugin_manager = get_plugin_manager([]) + plugin_manager = get_plugin_manager(options.plugins) work_folder = Path(mkdtemp(prefix="com.github.ocrmypdf.")) debug_log_handler = None diff --git a/src/ocrmypdf/builtin_plugins/tesseract_ocr.py b/src/ocrmypdf/builtin_plugins/tesseract_ocr.py index 85b2e4c8..ee97845b 100644 --- a/src/ocrmypdf/builtin_plugins/tesseract_ocr.py +++ b/src/ocrmypdf/builtin_plugins/tesseract_ocr.py @@ -16,10 +16,66 @@ # along with OCRmyPDF. If not, see . from ocrmypdf import hookimpl +from ocrmypdf.cli import numeric from ocrmypdf.exec import tesseract from ocrmypdf.pluginspec import OcrEngine +@hookimpl +def add_options(parser): + tess = parser.add_argument_group("Tesseract", "Advanced control of Tesseract OCR") + tess.add_argument( + '--tesseract-config', + action='append', + metavar='CFG', + default=[], + help="Additional Tesseract configuration files -- see documentation", + ) + tess.add_argument( + '--tesseract-pagesegmode', + action='store', + type=int, + metavar='PSM', + choices=range(0, 14), + help="Set Tesseract page segmentation mode (see tesseract --help)", + ) + tess.add_argument( + '--tesseract-oem', + action='store', + type=int, + metavar='MODE', + choices=range(0, 4), + help=( + "Set Tesseract 4.0 OCR engine mode: " + "0 - original Tesseract only; " + "1 - neural nets LSTM only; " + "2 - Tesseract + LSTM; " + "3 - default." + ), + ) + tess.add_argument( + '--tesseract-timeout', + default=180.0, + type=numeric(float, 0), + metavar='SECONDS', + help='Give up on OCR after the timeout, but copy the preprocessed page ' + 'into the final output', + ) + tess.add_argument( + '--user-words', + metavar='FILE', + help="Specify the location of the Tesseract user words file. This is a " + "list of words Tesseract should consider while performing OCR in " + "addition to its standard language dictionaries. This can improve " + "OCR quality especially for specialized and technical documents.", + ) + tess.add_argument( + '--user-patterns', + metavar='FILE', + help="Specify the location of the Tesseract user patterns file.", + ) + + class TesseractOcrEngine(OcrEngine): @staticmethod def version(): diff --git a/src/ocrmypdf/cli.py b/src/ocrmypdf/cli.py index cea8e351..0675300c 100644 --- a/src/ocrmypdf/cli.py +++ b/src/ocrmypdf/cli.py @@ -57,6 +57,7 @@ class ArgumentParser(argparse.ArgumentParser): def get_parser(): parser = ArgumentParser( prog=_PROGRAM_NAME, + allow_abbrev=True, fromfile_prefix_chars='@', formatter_class=argparse.RawDescriptionHelpFormatter, description="""\ @@ -382,14 +383,14 @@ Online documentation is located at: ) advanced = parser.add_argument_group( - "Advanced", "Advanced options to control Tesseract's OCR behavior" + "Advanced", "Advanced options to control OCRmyPDF" ) advanced.add_argument( '--pages', type=str, help=( "Limit OCR to the specified pages (ranges or comma separated), " - "skipping others", + "skipping others" ), ) advanced.add_argument( @@ -401,35 +402,6 @@ Online documentation is located at: "decompression bomb", default=128.0, ) - advanced.add_argument( - '--tesseract-config', - action='append', - metavar='CFG', - default=[], - help="Additional Tesseract configuration files -- see documentation", - ) - advanced.add_argument( - '--tesseract-pagesegmode', - action='store', - type=int, - metavar='PSM', - choices=range(0, 14), - help="Set Tesseract page segmentation mode (see tesseract --help)", - ) - advanced.add_argument( - '--tesseract-oem', - action='store', - type=int, - metavar='MODE', - choices=range(0, 4), - help=( - "Set Tesseract 4.0 OCR engine mode: " - "0 - original Tesseract only; " - "1 - neural nets LSTM only; " - "2 - Tesseract + LSTM; " - "3 - default." - ), - ) advanced.add_argument( '--pdf-renderer', choices=['auto', 'hocr', 'sandwich'], @@ -437,14 +409,6 @@ Online documentation is located at: help="Choose OCR PDF renderer - the default option is to let OCRmyPDF " "choose. See documentation for discussion.", ) - advanced.add_argument( - '--tesseract-timeout', - default=180.0, - type=numeric(float, 0), - metavar='SECONDS', - help='Give up on OCR after the timeout, but copy the preprocessed page ' - 'into the final output', - ) advanced.add_argument( '--rotate-pages-threshold', default=14.0, @@ -466,19 +430,6 @@ Online documentation is located at: "skipped. Not supported for --output-type=pdf ; that setting " "preserves the original compression of all images.", ) - advanced.add_argument( - '--user-words', - metavar='FILE', - help="Specify the location of the Tesseract user words file. This is a " - "list of words Tesseract should consider while performing OCR in " - "addition to its standard language dictionaries. This can improve " - "OCR quality especially for specialized and technical documents.", - ) - advanced.add_argument( - '--user-patterns', - metavar='FILE', - help="Specify the location of the Tesseract user patterns file.", - ) advanced.add_argument( '--fast-web-view', type=numeric(float, 0), diff --git a/tests/conftest.py b/tests/conftest.py index 55bd8376..d7d4b437 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,7 +25,7 @@ from subprocess import PIPE, run import pytest from ocrmypdf import api, cli, pdfinfo -from ocrmypdf._plugin_manager import get_plugin_manager +from ocrmypdf._plugin_manager import get_parser_options_plugins from ocrmypdf.exec import unpaper pytest_plugins = ['helpers_namespace'] @@ -213,12 +213,11 @@ def no_outpdf(tmp_path): @pytest.helpers.register def check_ocrmypdf(input_file, output_file, *args, env=None): """Run ocrmypdf and confirmed that a valid file was created""" + args = [str(input_file), str(output_file)] + [ + str(arg) for arg in args if arg is not None + ] - options = cli.get_parser().parse_args( - [str(input_file), str(output_file)] - + [str(arg) for arg in args if arg is not None] - ) - plugin_manager = get_plugin_manager(options.plugins) + _parser, options, plugin_manager = get_parser_options_plugins(args=args) api.check_options(options, plugin_manager) if env: options.tesseract_env = env @@ -239,10 +238,10 @@ def run_ocrmypdf_api(input_file, output_file, *args, env=None): Does not currently have a way to manipulate the PATH except for Tesseract. """ - options = cli.get_parser().parse_args( - [str(input_file), str(output_file)] - + [str(arg) for arg in args if arg is not None] - ) + args = [str(input_file), str(output_file)] + [ + str(arg) for arg in args if arg is not None + ] + _parser, options, plugin_manager = get_parser_options_plugins(args=args) if env: options.tesseract_env = env.copy() options.tesseract_env['_OCRMYPDF_TEST_INFILE'] = os.fspath(input_file) @@ -253,7 +252,6 @@ def run_ocrmypdf_api(input_file, output_file, *args, env=None): if options.tesseract_env: assert all(isinstance(v, (str, bytes)) for v in options.tesseract_env.values()) - plugin_manager = get_plugin_manager(options.plugins) api.check_options(options, plugin_manager) return api.run_pipeline(options, plugin_manager=None, api=False) diff --git a/tests/test_validation.py b/tests/test_validation.py index 3d3221a4..857dbda0 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -32,8 +32,11 @@ from ocrmypdf.pdfinfo import PdfInfo def make_opts(input_file='a.pdf', output_file='b.pdf', language='eng', **kwargs): if language is not None: kwargs['language'] = language + parser = get_parser() + pm = get_plugin_manager(kwargs.get('plugins', [])) + pm.hook.add_options(parser=parser) return create_options( - input_file=input_file, output_file=output_file, parser=get_parser(), **kwargs + input_file=input_file, output_file=output_file, parser=parser, **kwargs )