123 lines
4.1 KiB
Python
123 lines
4.1 KiB
Python
# SPDX-FileCopyrightText: 2022 James R. Barlow
|
|
# SPDX-License-Identifier: MPL-2.0
|
|
|
|
"""Defines context objects that are passed to child processes/threads."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterator
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
from ocrmypdf._options import OcrOptions
|
|
from ocrmypdf.pdfinfo import PdfInfo
|
|
from ocrmypdf.pdfinfo.info import PageInfo
|
|
|
|
if TYPE_CHECKING:
|
|
from ocrmypdf._plugin_manager import OcrmypdfPluginManager
|
|
|
|
|
|
class PdfContext:
|
|
"""Holds the context for a particular run of the pipeline."""
|
|
|
|
options: OcrOptions #: The specified options for processing this PDF.
|
|
origin: Path #: The filename of the original input file.
|
|
pdfinfo: PdfInfo #: Detailed data for this PDF.
|
|
plugin_manager: (
|
|
OcrmypdfPluginManager #: PluginManager for processing the current PDF.
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
options: OcrOptions,
|
|
work_folder: Path,
|
|
origin: Path,
|
|
pdfinfo: PdfInfo,
|
|
plugin_manager,
|
|
):
|
|
self.options = options
|
|
self.options.work_folder = work_folder
|
|
self.work_folder = work_folder
|
|
self.origin = origin
|
|
self.pdfinfo = pdfinfo
|
|
self.plugin_manager = plugin_manager
|
|
|
|
def get_path(self, name: str) -> Path:
|
|
"""Generate a ``Path`` for an intermediate file involved in processing.
|
|
|
|
The path will be in a temporary folder that is common for all processing
|
|
of this particular PDF.
|
|
"""
|
|
return self.work_folder / name
|
|
|
|
def get_page_contexts(self) -> Iterator[PageContext]:
|
|
"""Get all ``PageContext`` for this PDF."""
|
|
npages = len(self.pdfinfo)
|
|
for n in range(npages):
|
|
yield PageContext(self, n)
|
|
|
|
def get_page_context_args(self) -> Iterator[tuple[PageContext]]:
|
|
"""Get all ``PageContext`` for this PDF packaged in tuple for args-splatting."""
|
|
npages = len(self.pdfinfo)
|
|
for n in range(npages):
|
|
yield (PageContext(self, n),)
|
|
|
|
|
|
class PageContext:
|
|
"""Holds our context for a page.
|
|
|
|
Must be pickle-able, so stores only intrinsic/simple data elements or those
|
|
capable of their serializing themselves via ``__getstate__``.
|
|
|
|
Note: Uses OcrOptions with JSON serialization for multiprocessing compatibility.
|
|
"""
|
|
|
|
origin: Path #: The filename of the original input file.
|
|
pageno: int #: This page number (zero-based).
|
|
pageinfo: PageInfo #: Information on this page.
|
|
plugin_manager: (
|
|
OcrmypdfPluginManager #: PluginManager for processing the current PDF.
|
|
)
|
|
|
|
def __init__(self, pdf_context: PdfContext, pageno):
|
|
self.work_folder = pdf_context.work_folder
|
|
self.origin = pdf_context.origin
|
|
# Store OcrOptions directly instead of Namespace
|
|
self.options = pdf_context.options
|
|
self.pageno = pageno
|
|
self.pageinfo = pdf_context.pdfinfo[pageno]
|
|
self.plugin_manager = pdf_context.plugin_manager
|
|
# Ensure no reference to PdfContext which contains OcrOptions
|
|
self._pdf_context = None
|
|
|
|
def get_path(self, name: str) -> Path:
|
|
"""Generate a ``Path`` for a file that is part of processing this page.
|
|
|
|
The path will be based in a common temporary folder and have a prefix based
|
|
on the page number.
|
|
"""
|
|
return self.work_folder / f"{(self.pageno + 1):06d}_{name}"
|
|
|
|
def __getstate__(self):
|
|
state = self.__dict__.copy()
|
|
|
|
options_json = self.options.model_dump_json_safe()
|
|
state['options_json'] = options_json
|
|
# Remove the OcrOptions object to avoid pickle issues
|
|
del state['options']
|
|
|
|
# Remove any potential references to Pydantic objects
|
|
state.pop('_pdf_context', None)
|
|
return state
|
|
|
|
def __setstate__(self, state):
|
|
self.__dict__.update(state)
|
|
|
|
# Reconstruct OcrOptions from JSON if available
|
|
if 'options_json' in state:
|
|
from ocrmypdf._options import OcrOptions
|
|
|
|
self.options = OcrOptions.model_validate_json_safe(state['options_json'])
|
|
# Otherwise, we have a fallback Namespace (shouldn't happen in normal operation)
|
|
# Leave it as-is for compatibility
|