refactor: replace Namespace with OCROptions in plugins and validation

Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
This commit is contained in:
James R. Barlow
2025-12-21 12:21:46 -08:00
co-authored by aider
parent d4b7165d72
commit 7b37f57b1c
4 changed files with 39 additions and 43 deletions
+11 -23
View File
@@ -10,7 +10,6 @@ import locale
import logging
import os
import sys
from argparse import Namespace
from collections.abc import Sequence
from pathlib import Path
from shutil import copyfileobj
@@ -50,7 +49,7 @@ def check_platform() -> None:
def check_options_languages(
options: Union[Namespace, OCROptions], ocr_engine_languages: list[str]
options: Union[OCROptions], ocr_engine_languages: list[str]
) -> None:
if not ocr_engine_languages:
return
@@ -76,7 +75,7 @@ def check_options_languages(
def check_options_sidecar(options: Union[Namespace, OCROptions]) -> None:
def check_options_sidecar(options: Union[OCROptions]) -> None:
if options.sidecar == '\0':
if options.output_file == '-':
raise BadArgsError("--sidecar filename needed when output file is stdout.")
@@ -91,7 +90,7 @@ def check_options_sidecar(options: Union[Namespace, OCROptions]) -> None:
)
def check_options_preprocessing(options: Union[Namespace, OCROptions]) -> None:
def check_options_preprocessing(options: Union[OCROptions]) -> None:
if options.clean_final:
options.clean = True
if options.unpaper_args and not options.clean:
@@ -119,37 +118,26 @@ def check_options_preprocessing(options: Union[Namespace, OCROptions]) -> None:
def _check_plugin_invariant_options(options: Union[Namespace, OCROptions]) -> None:
def _check_plugin_invariant_options(options: Union[OCROptions]) -> None:
check_platform()
check_options_sidecar(options)
check_options_preprocessing(options)
def _check_plugin_options(
options: Union[Namespace, OCROptions], plugin_manager: PluginManager
options: OCROptions, plugin_manager: PluginManager
) -> None:
# Convert to Namespace for plugin compatibility during transition
if isinstance(options, OCROptions):
legacy_options = options.to_namespace()
else:
legacy_options = options
plugin_manager.hook.check_options(options=legacy_options)
ocr_engine_languages = plugin_manager.hook.get_ocr_engine().languages(
legacy_options
)
plugin_manager.hook.check_options(options=options)
ocr_engine_languages = plugin_manager.hook.get_ocr_engine().languages(options)
check_options_languages(options, ocr_engine_languages)
def check_options(
options: Union[Namespace, OCROptions], plugin_manager: PluginManager
) -> None:
def check_options(options: OCROptions, plugin_manager: PluginManager) -> None:
_check_plugin_invariant_options(options)
_check_plugin_options(options, plugin_manager)
def create_input_file(
options: Union[Namespace, OCROptions], work_folder: Path
) -> tuple[Path, str]:
def create_input_file(options: OCROptions, work_folder: Path) -> tuple[Path, str]:
if options.input_file == '-':
# stdin
log.info('reading file from standard input')
@@ -194,7 +182,7 @@ def create_input_file(
raise InputFileError(msg) from e
def check_requested_output_file(options: Union[Namespace, OCROptions]) -> None:
def check_requested_output_file(options: OCROptions) -> None:
if options.output_file == '-':
if sys.stdout.isatty():
raise BadArgsError(
@@ -212,7 +200,7 @@ def check_requested_output_file(options: Union[Namespace, OCROptions]) -> None:
def report_output_file_size(
options: Union[Namespace, OCROptions],
options: OCROptions,
input_file: Path,
output_file: Path,
optimize_messages: Sequence[str] | None = None,
+5 -2
View File
@@ -74,8 +74,6 @@ def check_options(options):
"use --force-ocr to discard existing text."
)
if options.output_type == 'pdfa':
options.output_type = 'pdfa-2'
if options.color_conversion_strategy not in ghostscript.COLOR_CONVERSION_STRATEGIES:
raise ValueError(
f"Invalid color conversion strategy: {options.color_conversion_strategy}"
@@ -128,6 +126,11 @@ def generate_pdfa(
stop_on_soft_error,
):
"""Generate a PDF/A from the list of PDF pages and PDF/A metadata."""
# Normalize output_type at point of use
output_type = context.options.output_type
if output_type == 'pdfa':
output_type = 'pdfa-2'
ghostscript.generate_pdfa(
pdf_pages=[pdfmark, *pdf_pages],
output_file=output_file,
+13 -9
View File
@@ -152,14 +152,6 @@ def check_options(options):
"Please upgrade to a newer or supported older version."
)
# Decide on what renderer to use
if options.pdf_renderer == 'auto':
if {'ara', 'heb', 'fas', 'per'} & set(options.languages):
log.info("Using sandwich renderer since there is an RTL language")
options.pdf_renderer = 'sandwich'
else:
options.pdf_renderer = 'hocr'
if not tesseract.has_thresholding() and options.tesseract_thresholding != 0:
log.warning(
"The installed version of Tesseract does not support changes to its "
@@ -234,9 +226,21 @@ class TesseractOcrEngine(OcrEngine):
def version():
return str(tesseract.version())
@staticmethod
def _determine_renderer(options):
"""Determine the PDF renderer to use based on options and languages."""
if options.pdf_renderer == 'auto':
if {'ara', 'heb', 'fas', 'per'} & set(options.languages):
log.info("Using sandwich renderer since there is an RTL language")
return 'sandwich'
else:
return 'hocr'
return options.pdf_renderer
@staticmethod
def creator_tag(options):
tag = '-PDF' if options.pdf_renderer == 'sandwich' else '-hOCR'
renderer = TesseractOcrEngine._determine_renderer(options)
tag = '-PDF' if renderer == 'sandwich' else '-hOCR'
return f"Tesseract OCR{tag} {TesseractOcrEngine.version()}"
def __str__(self):
+10 -9
View File
@@ -6,7 +6,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from argparse import ArgumentParser, Namespace
from argparse import ArgumentParser
from collections.abc import Sequence, Set
from logging import Handler
from pathlib import Path
@@ -15,6 +15,7 @@ from typing import TYPE_CHECKING, NamedTuple
import pluggy
from ocrmypdf import Executor, PdfContext
from ocrmypdf._options import OCROptions
from ocrmypdf._progressbar import ProgressBar
from ocrmypdf.helpers import Resolution
@@ -87,7 +88,7 @@ def add_options(parser: ArgumentParser) -> None:
@hookspec
def check_options(options: Namespace) -> None:
def check_options(options: OCROptions) -> None:
"""Called to ask the plugin to check all of the options.
The plugin may check if options that it added are valid.
@@ -158,7 +159,7 @@ def get_progressbar_class() -> type[ProgressBar]:
@hookspec
def validate(pdfinfo: PdfInfo, options: Namespace) -> None:
def validate(pdfinfo: PdfInfo, options: OCROptions) -> None:
"""Called to give a plugin an opportunity to review *options* and *pdfinfo*.
*options* contains the "work order" to process a particular file. *pdfinfo*
@@ -368,7 +369,7 @@ class OcrEngine(ABC):
@staticmethod
@abstractmethod
def creator_tag(options: Namespace) -> str:
def creator_tag(options: OCROptions) -> str:
"""Returns the creator tag to identify this software's role in creating the PDF.
This tag will be inserted in the XMP metadata and DocumentInfo dictionary
@@ -389,7 +390,7 @@ class OcrEngine(ABC):
@staticmethod
@abstractmethod
def languages(options: Namespace) -> Set[str]:
def languages(options: OCROptions) -> Set[str]:
"""Returns the set of all languages that are supported by the engine.
Languages are typically given in 3-letter ISO 3166-1 codes, but actually
@@ -398,18 +399,18 @@ class OcrEngine(ABC):
@staticmethod
@abstractmethod
def get_orientation(input_file: Path, options: Namespace) -> OrientationConfidence:
def get_orientation(input_file: Path, options: OCROptions) -> OrientationConfidence:
"""Returns the orientation of the image."""
@staticmethod
def get_deskew(input_file: Path, options: Namespace) -> float:
def get_deskew(input_file: Path, options: OCROptions) -> float:
"""Returns the deskew angle of the image, in degrees."""
return 0.0
@staticmethod
@abstractmethod
def generate_hocr(
input_file: Path, output_hocr: Path, output_text: Path, options: Namespace
input_file: Path, output_hocr: Path, output_text: Path, options: OCROptions
) -> None:
"""Called to produce a hOCR file from a page image and sidecar text file.
@@ -432,7 +433,7 @@ class OcrEngine(ABC):
@staticmethod
@abstractmethod
def generate_pdf(
input_file: Path, output_pdf: Path, output_text: Path, options: Namespace
input_file: Path, output_pdf: Path, output_text: Path, options: OCROptions
) -> None:
"""Called to produce a text only PDF from a page image.