插件

本文档中的关键词“必须”、“不得”、“要求”、“应”、“不应”、“应该”、“不应该”、“推荐”、“可以”和“可选”应按照RFC 2119中的描述进行解释。

您可以使用插件在OCRmyPDF的某些关键点自定义其行为。

目前,可以实现以下功能:

  • 添加新的命令行参数

  • 覆盖是否对特定文件执行OCR的决定

  • 修改即将发送用于OCR的图像

  • 在页面转换为PDF之前修改页面图像

  • 用具有类似行为的另一个OCR引擎替换Tesseract OCR

  • 用另一个PDF到图像转换器(光栅化器)或PDF/A生成器替换Ghostscript

OCRmyPDF 插件基于 Python 的 pluggy 包,并遵循其约定。请注意:目前未检查通过 setuptools 入口点安装的插件,因为 OCRmyPDF 假设您可能不希望为所有文件启用插件。

请参阅 [OCRmyPDF-EasyOCR](https://github.com/ocrmypdf/OCRmyPDF-EasyOCR) 以获取一个简单且完全可用的插件示例。

脚本插件

脚本插件可以通过指定文件名从命令行调用。脚本插件对于非正式或“一次性”插件可能很方便,例如当某批文件需要特殊处理步骤时。

ocrmypdf --plugin ocrmypdf_example_plugin.py input.pdf output.pdf

可以通过多次发出--plugin参数来安装多个插件。

打包插件

已安装的插件可能会安装到与OCRmyPDF相同的虚拟环境中。它们可以使用Python标准模块命名来调用。如果您打算分发插件,请将其打包。

ocrmypdf --plugin ocrmypdf_fancypants.pockets.contents input.pdf output.pdf

OCRmyPDF 不会自动导入插件,因为假设是插件对不同文件的影响不同,您可能不希望它们一直处于激活状态。必须通过命令行或 ocrmypdf.ocr(plugin='...') 来调用它们。

希望为 ocrmypdf 分发包的第三方应将其打包为插件包,这些模块的名称应以 ocrmypdf_ 开头,类似于 pytest 包,例如 pytest-cov(包)和 pytest_cov(模块)。

注意

我们建议插件作者在命名他们的插件时使用前缀 ocrmypdf-(用于PyPI上的包名称)和ocrmypdf_(用于 模块),就像pytest插件一样。同时,请明确说明 您的包不是官方的。

插件

你也可以创建一个插件,如果OCRmyPDF和插件都安装在同一个虚拟环境中,OCRmyPDF将始终自动加载该插件,使用项目入口点。 OCRmyPDF使用入口点命名空间“ocrmypdf”。

例如,对于一个名为 ocrmypdf-exampleplugin 的插件,pyproject.toml 需要包含以下内容:

[project]
name = "ocrmypdf-exampleplugin"

[project.entry-points."ocrmypdf"]
exampleplugin = "exampleplugin.pluginmodule"

插件要求

OCRmyPDF 通常使用多个工作进程。当启动一个新工作进程时,Python 会再次导入所有插件,包括之前已经导入的所有插件。这意味着一个工作进程中插件的全局状态不会与其他工作进程共享。因此,插件钩子实现应该是无状态的,仅依赖于它们的输入。钩子实现可以使用它们的输入参数来获取由另一个钩子实现准备的共享状态的引用。插件必须预期其他插件实例将同时运行。

传递给许多钩子的context对象可用于共享有关正在处理的文件的信息。插件必须将私有的、特定于插件的数据写入名为{options.work_folder}/ocrmypdf-plugin-name的子文件夹中。插件可以读取和写入options.work_folder中的文件,但应注意它们的语义可能会发生变化。

OCRmyPDF 在完成文件的 OCR 处理后会删除 options.work_folder,除非使用 --keep-temporary-files 调用。

一些插件钩子的文档包含了它们被调用时的执行上下文的详细描述。

插件应准备好无论在线程还是进程中执行都能正常工作。通常,OCRmyPDF 使用进程,但有一个半隐藏的线程参数可以简化调试。

插件钩子

一个插件可以提供以下钩子。钩子必须用 ocrmypdf.hookimpl 装饰,例如:

from ocrmpydf import hookimpl

@hookimpl
def add_options(parser):
    pass

以下是所有可用的钩子的完整列表,以及它们被调用的时间。

关于firstresult钩子的说明

如果多个插件为此钩子安装了实现,它们将按照安装顺序的相反顺序被调用(即,最后一个插件获胜)。当每个钩子实现按顺序被调用时,第一个返回非None值的实现将“获胜”并阻止所有其他钩子的执行。因此,你不能以这种方式将一系列插件过滤器“链接”在一起。相反,单个钩子实现应负责任何此类链接操作。

示例

  • OCRmyPDF的测试套件包含多个插件,用于模拟某些测试条件。

  • ocrmypdf-papermerge 是一个生产插件,它集成了 OCRmyPDF 和 Papermerge 文档管理系统。

抑制或覆盖其他插件

ocrmypdf.pluginspec.initialize(plugin_manager: PluginManager) None

当此插件首次加载到OCRmyPDF时调用。

此功能的主要用途是供插件检查与其他插件的兼容性,并可能阻止其他模块。一个希望阻止ocrmypdf内置优化插件的插件可以这样做:

plugin_manager.set_blocked('ocrmypdf.builtin_plugins.optimize')

插件实现检查是否无法继续也是合理的,例如,因为缺少必需的依赖项。(如果插件继续的能力取决于选项和参数,请使用validate代替。)

Raises:

ocrmypdf.exceptions.ExitCodeException – 如果选项不可接受 并且应用程序应该以信息性消息和错误代码优雅地终止。

注意

这个钩子将从主进程调用,并可能在子工作进程被分叉之前修改全局状态。

自定义命令行参数

ocrmypdf.pluginspec.add_options(parser: ArgumentParser) None

允许插件添加其自己的命令行和API参数。

OCRmyPDF 将命令行参数转换为 API 参数,因此在此处添加参数将导致在 API 调用 ocrmypdf.ocr 或在命令行上调用时处理新参数。

注意

这个钩子将从主进程调用,并可能在子工作进程被分叉之前修改全局状态。

ocrmypdf.pluginspec.check_options(options: Namespace) None

调用以请求插件检查所有选项。

插件可能会检查它添加的选项是否有效。

警告或其他消息可以通过创建一个日志记录器对象传递给用户,使用log = logging.getLogger(__name__)并记录到此对象。

插件也可能修改options。options中的所有对象必须是可序列化的,以便它们可以被编组到子工作进程。

Raises:

ocrmypdf.exceptions.ExitCodeException – 如果选项不可接受 并且应用程序应该优雅地终止,并提供一个信息性的 消息和错误代码。

注意

这个钩子将从主进程调用,并可能在子工作进程被分叉之前修改全局状态。

执行和进度报告

class ocrmypdf.pluginspec.ProgressBar(*args, **kwargs)

OCRmyPDF期望进度条类与之兼容的协议。

实际上,这可以用于任何类型的监控,而不仅仅是进度条。

调用该类应返回一个新的进度条对象,该对象通过__enter__激活并通过__exit__终止。每当进度条更新时,都会调用更新方法。进度条对象不会被重复使用;每个任务组都会创建一个新的进度条对象。

进度条由主进程/线程持有,不会被子进程/线程更新。当一个子进程通知父进程工作完成时,父进程会更新进度条。

进度条不应写入sys.stdout,否则如果OCRmyPDF将PDF写入标准输出,它们将破坏输出。

OCRmyPDF报告给进度条的事件类型可能会在次要版本中发生变化。

__enter__()

进入进度条上下文。

__exit__(*args)

退出进度条上下文。

__init__(*args, **kwargs)
update(n=1, *, completed=None)

通过增量更新进度条。

用于进度条上下文。

class ocrmypdf.pluginspec.Executor(*, pbar_class=None)

抽象的并发执行器。

__call__(*, use_threads: bool, max_workers: int, progress_kwargs: dict, worker_initializer: Callable | None = None, task: Callable[[...], T] | None = None, task_arguments: Iterable | None = None, task_finished: Callable[[T, ProgressBar], None] | None = None) None

设置并行执行和进度报告。

Parameters:
  • use_threads – 如果 False,工作负载是那种从多进程环境中运行中受益的类型(例如,它大量使用 Python,并且使用线程并行化预计不会提高性能)。

  • max_workers – 应该运行的最大工作线程数。

  • progress_kwargs – 用于设置进度条的参数。

  • worker_initializer – 当工作线程初始化时调用,在工作线程的执行上下文中。如果子工作线程是进程,则必须能够序列化/腌制工作线程初始化器。 functools.partial 可用于绑定参数。

  • task – 当工作线程开始一个新任务时调用,在工作线程的执行上下文中。必须能够序列化到工作线程。

  • task_finished – 当工作线程完成任务时调用,在父上下文中。

  • task_arguments – 一个可迭代对象,为每个任务生成一组参数。这将在父上下文中运行,但参数必须能够序列化到工作节点。

pbar_class

NullProgressBar 的别名

ocrmypdf.pluginspec.get_logging_console() Handler

返回一个自定义的日志处理程序。

通常,当日志输出和进度条都输出到 sys.stderr 时,这是必要的。

注意

这是一个firstresult hook

ocrmypdf.pluginspec.get_executor(progressbar_class: type[ProgressBar]) Executor

调用以获取管理并行执行的对象。

这可以用于用第三方替代方案替换OCRmyPDF的默认并行执行系统。例如,您可以使OCRmyPDF在分布式环境中运行。

OCRmyPDF的执行器类似于conconcurrent.futures中的标准Python执行器,但它们的工作方式不同。执行器可以重复用于不同的、不相关的批量操作,因为给定作业的所有上下文都传递给Executor.__call__()

应为类型 Executor 或符合该调用的协议。

Parameters:

progressbar_class – 一个进度条类,将在创建时使用

注意

这个钩子将从主进程调用,并可能在子工作进程被分叉之前修改全局状态。

注意

这是一个firstresult hook

ocrmypdf.pluginspec.get_progressbar_class() type[ProgressBar]

调用以获取可用于监视进度的类。

OCRmyPDF 在需要显示进度条时会调用此函数。 此函数返回的类必须与 ProgressBar 协议兼容。

以下是OCRmyPDF将如何使用进度条:

示例

pbar_class = pm.hook.get_progressbar_class() 使用 pbar_class(**progress_kwargs) 作为 pbar:

… pbar.update(1)

在处理前应用特殊行为

ocrmypdf.pluginspec.validate(pdfinfo: PdfInfo, options: Namespace) None

调用以给插件一个机会来审查optionspdfinfo

options 包含处理特定文件的“工作指令”。pdfinfo 包含加载和解析后获得的输入文件信息。插件可以修改 options。例如,您可以根据 pdfinfo 中的信息决定某种类型的文件应使用 options.force_ocr = True 进行处理。

Raises:

ocrmypdf.exceptions.ExitCodeException – 如果选项或pdfinfo不可接受 并且应用程序应该优雅地终止,提供信息性 消息和错误代码。

注意

这个钩子将从主进程调用,并可能在子工作进程被分叉之前修改全局状态。

PDF页面转图像

ocrmypdf.pluginspec.rasterize_pdf_page(input_file: Path, output_file: Path, raster_device: str, raster_dpi: Resolution, pageno: int, page_dpi: Resolution | None, rotation: int | None, filter_vector: bool, stop_on_soft_error: bool) Path

在画布单位中以分辨率raster_dpi栅格化PDF的一页。

图像的大小被调整为匹配由raster_dpi暗示的整数像素尺寸,即使这些数字是非整数的。图像的DPI将被page_dpi中的值覆盖。

Parameters:
  • input_file – 要光栅化的PDF文件。

  • output_file – 栅格化图像的期望名称。

  • raster_device – 在output_file生成的图像类型。

  • raster_dpi – 以每英寸点数表示的分辨率,用于栅格化页面。

  • pageno – 要光栅化的页码(从第1页开始)。

  • page_dpi – 分辨率,覆盖输出图像的DPI。

  • rotation – 旋转页面的基本角度,顺时针方向。

  • filter_vector – 如果为True,移除矢量图形对象。

  • stop_on_soft_error – 如果存在“软错误”,例如PDF页面图像生成可以继续进行,但可能与原始图像在视觉上有所不同,此钩子的实现者应引发详细的异常。如果False,则继续处理并通过日志记录报告。如果钩子无法继续,无论此设置如何,都应始终引发异常。一个“软错误”可能是缺少正确栅格化PDF所需的字体。

Returns:

路径 – 如果成功则为 output_file

注意

这个钩子将从子进程中调用。修改全局状态不会影响主进程或其他子进程。

注意

这是一个firstresult hook

修改中间图像

ocrmypdf.pluginspec.filter_ocr_image(page: PageContext, image: Image.Image) Image.Image

在图像发送到OCR之前调用以进行过滤。

这是OCR看到的图像,而不是用户查看PDF时看到的图像。在某些模式下,例如--redo-ocr,图像的部分可能会被遮罩以隐藏它们不被OCR识别。

此钩子的主要用途预计包括隐藏内容以防止OCR识别,通过过滤器调整图像以更好地适应OCR,以及调整图像以匹配OCR引擎施加的任何限制。

输入的图像可能是彩色的、灰度的或单色的,输出的图像可能会有所不同。例如,如果您知道自定义OCR引擎不关心文本的颜色,您可以将图像转换为灰度或单色。

一般来说,输出图像应该是输入图像的忠实表示。你可以改变输入图像的像素宽度和高度,但你不能改变宽高比,并且你必须根据新的像素宽度和高度计算输出图像的DPI,否则OCR文本层将与视觉位置不对齐。

内置的Tesseract OCR引擎使用此钩子本身来下采样非常大的图像以适应其限制。

注意

这个钩子将从子进程中调用。修改全局状态不会影响主进程或其他子进程。

注意

这是一个firstresult hook

ocrmypdf.pluginspec.filter_page_image(page: PageContext, image_filename: Path) Path

在将整个页面插入PDF之前调用以进行过滤。

只有在发出预处理命令行参数或发出--force-ocr时,才会生成整页图像。如果没有为给定页面生成整页图像,则不会调用此函数。这不是将显示给OCR的图像。

如果函数不想修改图像,它应该返回 image_filename。钩子可以用一个新文件覆盖image_filename

输出图像应保持相同的物理单位尺寸,即 (width * dpi_x, height * dpi_y)。也就是说,如果图像被调整大小,DPI 必须按倒数调整。如果不保持这一点,PDF页面 将被调整大小,OCR层将不对齐。OCRmyPDF不会 强制执行这些约束;插件需要做合理的事情。

OCRmyPDF 将根据所使用的图像格式创建 PDF 页面(除非覆盖了钩子)。如果您将图像转换为 JPEG,输出页面将创建为 JPEG 等。如果您更改了色彩空间,该更改将被保留。请注意,如果启用了 OCRmyPDF 图像优化阶段,最终可能会选择不同的格式。

如果返回值是一个不存在的文件,将会发生FileNotFoundError。返回值应该是与image_filename在同一文件夹中的文件路径。

注意

这个钩子将从子进程中调用。修改全局状态不会影响主进程或其他子进程。

注意

这是一个firstresult hook

ocrmypdf.pluginspec.filter_pdf_page(page: PageContext, image_filename: Path, output_pdf: Path) Path

调用以将过滤后的整页图像转换为PDF。

只有在发出预处理命令行参数或发出--force-ocr时,才会生成整页图像。如果没有为给定页面生成整页图像,则不会调用此函数。这不是将显示给OCR的图像。整页图像在上面的钩子filter_page_image中进行过滤,然后调用此函数进行PDF转换。

此函数仅在 OCRmyPDF 运行于“强制 OCR”模式等所有内容进行光栅化的模式下调用。

在这个阶段可以做一些聪明的事情,比如将页面图像分割成颜色区域或矢量等效物。

钩子实现的提供者负责确保OCR文本层与此处生成的PDF对齐,否则将导致文本错位。

目前,此函数必须生成单页PDF,否则管道将失败。如果目的是删除PDF,则创建一个单页空PDF。

Parameters:
  • page – 此页面的上下文。

  • image_filename – 用于创建output_pdf的输入图像的文件名,如果完全重新创建output_pdf,则作为“参考”。

  • output_pdf – 之前创建的 output_pdf。

Returns:

输出PDF

注意

这个钩子将从子进程中调用。修改全局状态不会影响主进程或其他子进程。

注意

这是一个firstresult hook

OCR引擎

ocrmypdf.pluginspec.get_ocr_engine() OcrEngine

返回用于处理此文件的OcrEngine。

OcrEngine 可以被主进程和子进程多次实例化。

注意

这是一个firstresult hook

class ocrmypdf.pluginspec.OcrEngine

一个表示具有与Tesseract OCR类似功能的OCR引擎的类。

这可以用于为另一个OCR引擎创建插件,而不是Tesseract OCR。

abstract __str__()

返回OCR引擎的名称和版本。

当 OCRmyPDF 想要向用户提及 OCR 引擎的名称时,通常会在错误消息中使用此功能。

abstract static creator_tag(options: Namespace) str

返回创建者标签以识别此软件在创建PDF中的角色。

此标签将根据情况插入到XMP元数据和DocumentInfo字典中。理想情况下,您应包括OCR引擎的名称及其版本。文本不应包含换行符。这是为了帮助像您这样的开发者识别生成此文件的软件。

OCRmyPDF 将始终将其名称添加到此值之前。

abstract static generate_hocr(input_file: Path, output_hocr: Path, output_text: Path, options: Namespace) None

调用以从页面图像和辅助文本文件生成hOCR文件。

hOCR文件是一种类似HTML的文件,用于描述页面上文本的位置。OCRmyPDF可以从hOCR文件创建一个仅包含文本的PDF,并将其嵌入到输出PDF中。

此函数在工作线程或工作进程中执行。OCRmyPDF 自动在页面上并行化OCR。OCR引擎不应 引入更多的并行性。

Parameters:
  • input_file – 用于执行OCR的页面图像。

  • output_hocr – 输出hOCR文件的预期名称。

  • output_text – 包含识别文本的文本文件的预期名称。

  • options – 命令行选项。

abstract static generate_pdf(input_file: Path, output_pdf: Path, output_text: Path, options: Namespace) None

用于从页面图像生成仅包含文本的PDF。

纯文本PDF不应包含任何可见材料,因为它将被移植到输入PDF页面上。它必须调整为输入图像的确切尺寸。

此函数在工作线程或工作进程中执行。OCRmyPDF 自动在页面上并行化OCR。OCR引擎不应 引入更多的并行性。

Parameters:
  • input_file – 用于执行OCR的页面图像。

  • output_pdf – 输出PDF的预期名称。

  • output_text – 包含识别文本的文本文件的预期名称。

  • options – 命令行选项。

static get_deskew(input_file: Path, options: Namespace) float

返回图像的去斜角度,单位为度。

abstract static get_orientation(input_file: Path, options: Namespace) OrientationConfidence

返回图像的方向。

abstract static languages(options: Namespace) Set[str]

返回引擎支持的所有语言的集合。

语言通常以3个字母的ISO 3166-1代码表示,但实际上可以是OCR引擎理解的任何值。

abstract static version() str

返回OCR引擎的版本。

class ocrmypdf.pluginspec.OrientationConfidence(angle: int, confidence: float)

表示OCR引擎对页面旋转的置信度。

angle

页面应旋转的顺时针角度(0, 90, 180, 270)。0表示不旋转。

Type:

int

confidence

OCR引擎对此正确旋转的置信度。0表示不自信,15表示非常自信。任意单位。

Type:

float

PDF/A 生产

ocrmypdf.pluginspec.generate_pdfa(pdf_pages: list[Path], pdfmark: Path, output_file: Path, context: PdfContext, pdf_version: str, pdfa_part: str, progressbar_class: type[ProgressBar] | None, stop_on_soft_error: bool) Path

生成一个PDF/A。

此API强烈假设使用具有Ghostscript语义的PDF/A生成器。

OCRmyPDF 将在生成后修改元数据,并可能对 PDF/A 进行线性化处理。

Parameters:
  • pdf_pages – 一个或多个文件名的列表,将被合并到输出文件中。

  • pdfmark – 一个用于Ghostscript的PostScript文件,详细说明了如何执行PDF/A转换。

  • output_file – 所需输出文件的名称。

  • context – 当前上下文。

  • pdf_version – 输出文件应具备的最低PDF版本。 PDF/A生成器可以根据自己的判断提高版本,但不应降低版本。

  • pdfa_part – 所需的PDF/A合规级别,例如 '2B'

  • progressbar_class – 进度条的类,必须实现ProgressBar协议。如果为None,则不报告进度。

  • stop_on_soft_error – 如果存在“软错误”,例如PDF/A生成可以继续进行并生成有效的PDF/A,但输出可能无效或可能在外观上与原始文件不符,此钩子的实现者应引发详细的异常。如果False,则继续处理并通过日志记录报告。如果钩子无法继续,无论此设置如何,它都应始终引发异常。

Returns:

路径 – 如果成功,钩子应返回 output_file

注意

这是一个firstresult hook

注意

在15.0.0版本之前,没有提供context,而是提供了compression。插件现在应该读取上下文对象来确定是否请求了压缩。

PDF优化

ocrmypdf.pluginspec.optimize_pdf(input_pdf: Path, output_pdf: Path, context: PdfContext, executor: Executor, linearize: bool) tuple[Path, Sequence[str]]

在图像、OCR和元数据处理后优化PDF。

如果 input_pdf 是一个 PDF/A,插件应该以保留 PDF/A 状态的方式修改 input_pdf,或者在不可能时向用户报告。

如果实现未能生成比输入文件更小的文件,则应返回 input_pdf。

一个实现新优化器的插件可能需要通过实现一个initialize钩子来抑制内置的优化器。

Parameters:
  • input_pdf – 输入的PDF,已添加OCR。

  • output_pdf – 应该由此优化钩子创建的输出PDF的请求文件名。

  • context – 当前上下文。

  • executor – 一个已初始化的执行器,可以在优化过程中使用,用于分发优化任务。

  • linearize – 如果为True,OCRmyPDF要求optimize_pdf返回一个线性化的,也称为快速网页视图的PDF。

Returns:

路径

如果优化成功,钩子应返回 output_file

如果优化没有产生更小的文件,钩子应返回 input_file

Sequence[str]: 插件希望向用户报告的任何评论,

特别是它无法进一步优化文件的原因。例如,插件可以报告所需的第三方未安装,因此未尝试特定的优化。

注意

这是一个firstresult hook

ocrmypdf.pluginspec.is_optimization_enabled(context: PdfContext) bool

对于给定的PdfContext,OCRmyPDF会询问插件是否启用了优化。

一个优化插件可能已安装并激活,但可能被用户设置禁用。

如果返回False,OCRmyPDF将采取某些措施来完成PDF。

Returns:

如果插件的优化已启用,则为True。

注意

这是一个firstresult hook