插件
本文件中出现的“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”等关键词应按照 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
(模块)。
注意
我们建议插件作者在 PyPI 上将其插件包命名为 ocrmypdf-
前缀,模块命名为 ocrmypdf_
前缀,就像 pytest 插件一样。同时,请明确表示您的包不是官方的。
插件
如果 OCRmyPDF 和插件都安装在同一虚拟环境中,您还可以创建一个 OCRmyPDF 将始终自动加载的插件,方法是使用项目入口点。OCRmyPDF 使用入口点命名空间“ocrmypdf”。
例如,对于名为 ocrmypdf-exampleplugin
的插件,pyproject.toml
需要包含以下内容
[project]
name = "ocrmypdf-exampleplugin"
[project.entry-points."ocrmypdf"]
exampleplugin = "exampleplugin.pluginmodule"
插件要求
OCRmyPDF 通常使用多个 worker 进程。当启动新的 worker 时,Python 将再次导入所有插件,包括所有之前导入的插件。这意味着一个 worker 中插件的全局状态不会与其它 worker 共享。因此,插件钩子实现应该是无状态的,仅依赖于其输入。钩子实现可以使用其输入参数来获取由另一个钩子实现准备的共享状态引用。插件必须预期插件的其他实例将同时运行。
传递给许多钩子的 context
对象可用于共享有关正在处理的文件的信息。插件必须将私有的、特定于插件的数据写入名为 {options.work_folder}/ocrmypdf-plugin-name
的子文件夹中。插件可以读取和写入 options.work_folder
中的文件,但应注意其语义可能会发生变化。
除非使用 --keep-temporary-files
调用,否则 OCRmyPDF 在完成对文件的 OCR 后将删除 options.work_folder
。
某些插件钩子的文档包含对其将被调用的执行上下文的详细描述。
插件应准备好无论是在 worker 线程还是 worker 进程中执行都能工作。通常,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
。)- 引发:
ocrmypdf.exceptions.ExitCodeException – 如果选项不可接受,并且应用程序应以信息性消息和错误代码正常终止。
注意
此钩子将从主进程调用,并且可以在派生子 worker 进程之前修改全局状态。
自定义命令行参数
- ocrmypdf.pluginspec.add_options(parser: ArgumentParser) None
允许插件添加自己的命令行和 API 参数。
OCRmyPDF 将命令行参数转换为 API 参数,因此在此处添加参数将导致处理对
ocrmypdf.ocr
的 API 调用或在命令行调用时处理新参数。注意
此钩子将从主进程调用,并且可以在派生子 worker 进程之前修改全局状态。
- ocrmypdf.pluginspec.check_options(options: Namespace) None
调用此钩子以要求插件检查所有选项。
插件可以检查它添加的选项是否有效。
可以通过使用
log = logging.getLogger(__name__)
创建日志记录器对象并将日志记录到此来将警告或其他消息传递给用户。插件还可以修改 options。options 中的所有对象都必须可 Pickle,以便可以将它们编组到子 worker 进程。
- 引发:
ocrmypdf.exceptions.ExitCodeException – 如果选项不可接受,并且应用程序应以信息性消息和错误代码正常终止。
注意
此钩子将从主进程调用,并且可以在派生子 worker 进程之前修改全局状态。
执行和进度报告
- class ocrmypdf.pluginspec.ProgressBar(*args, **kwargs)
OCRmyPDF 期望进度条类兼容的协议。
实际上,这可以用于任何类型的监控,而不仅仅是进度条。
调用类应返回一个新的进度条对象,该对象通过
__enter__
激活并通过__exit__
终止。每当进度条更新时都会调用 update 方法。进度条对象不会被重用;对于每组任务都会创建一个新的对象。进度条保留在主进程/线程中,而不是由子进程/线程更新。当子进程通知父进程完成的工作时,父进程更新进度条。进度条不应写入
sys.stdout
,否则如果 OCRmyPDF 将 PDF 写入标准输出,它将破坏输出。注意
OCRmyPDF 向进度条报告的事件类型可能会在
小版本中更改。
- 参数:
total (int | float | None) – 预期总工作单位数。如果为
None
,则总数未知。例如,如果您正在处理页面,这可能是页面数,或者如果您正在以百分比测量总进度,这可能是 100。desc (str | None) – 当前步骤的简要描述(例如“扫描内容”、“OCR”、“PDF/A 转换”)。OCRmyPDF 在每个主要步骤之前更新此项。
unit (str | None) – 正在跟踪的工作类型的简短标签(例如“页面”、“%”、“图像”)。
disable (bool) – 如果为
True
,则抑制进度更新(无输出)。默认为False
。**kwargs – OCRmyPDF 可能传递的未来或额外参数。实现应接受并优雅地忽略无法识别的关键字。
示例
一个简单的插件实现可能如下所示
from ocrmypdf.pluginspec import ProgressBar from ocrmypdf import hookimpl class ConsoleProgressBar(ProgressBar): def __init__(self, *, total=None, desc=None, unit=None, disable=False, **kwargs): self.total = total self.desc = desc self.unit = unit self.disable = disable self.current = 0 def __enter__(self): if not self.disable: print(f"Starting {self.desc or 'an OCR task'} (total={self.total} {self.unit})") return self def __exit__(self, exc_type, exc_value, traceback): if not self.disable: if exc_type is None: print("Completed successfully.") else: print(f"Task ended with error: {exc_value}") return False # Let OCRmyPDF raise any exceptions def update(self, n=1, *, completed=None): if completed is not None: # If 'completed' is given, you could set self.current = completed # but let's just read it to show usage print(f"Absolute completion reported: {completed}") # Otherwise, we increment by 'n' self.current += n if not self.disable: if self.total: percent = (self.current / self.total) * 100 print(f"{self.desc}: {self.current}/{self.total} ({percent:.1f}%)") else: print(f"{self.desc}: {self.current} units done") @hookimpl def get_progressbar_class(): return MyProgressBar
- __enter__()
进入进度条上下文。
- __exit__(*args)
退出进度条上下文。
- __init__(*args, **kwargs)
- 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
设置并行执行和进度报告。
- 参数:
use_threads – 如果为
False
,则工作负载属于在多进程环境中运行将受益的类型(例如,它大量使用 Python,并且预计使用线程并行化不会表现良好)。max_workers – 应运行的最大 worker 数。
progress_kwargs – 设置进度条的参数。
worker_initializer – 在 worker 初始化时调用,在 worker 的执行上下文中。如果子 worker 是进程,则 worker 初始化器必须可编组/Pickle。可以使用
functools.partial
来绑定参数。task – 当 worker 启动新任务时调用,在 worker 的执行上下文中。必须能够编组到 worker。
task_finished – 当 worker 完成任务时调用,在父进程的上下文中。
task_arguments – 一个可迭代对象,为每个任务生成一组参数。这在父进程的上下文中运行,但参数必须可编组到 worker。
- pbar_class
别名
NullProgressBar
- ocrmypdf.pluginspec.get_logging_console() Handler
返回一个自定义日志处理器。
通常,当日志输出和进度条都输出到
sys.stderr
时,这是必需的。注意
这是一个 firstresult 钩子。
- ocrmypdf.pluginspec.get_executor(progressbar_class: type[ProgressBar]) Executor
调用此钩子以获取管理并行执行的对象。
这可用于用第三方替代方案替换 OCRmyPDF 的默认并行执行系统。例如,您可以使 OCRmyPDF 在分布式环境中运行。
OCRmyPDF 的执行器类似于
conconcurrent.futures
中的标准 Python 执行器,但它们的工作方式不同。执行器可以重复用于不同的、不相关的批处理操作,因为给定作业的所有上下文都传递给了Executor.__call__()
。类型应为
Executor
或其他符合该调用协议的类型。- 参数:
progressbar_class – 进度条类,将在以下情况创建
注意
此钩子将从主进程调用,并且可以在派生子 worker 进程之前修改全局状态。
注意
这是一个 firstresult 钩子。
- ocrmypdf.pluginspec.get_progressbar_class() type[ProgressBar]
调用此函数以获取可用于监视进度的类。
当 OCRmyPDF 想要显示进度条时,将调用此函数。此函数返回的类必须与
ProgressBar
协议兼容。示例
以下是 OCRmyPDF 如何使用进度条
pbar_class = pm.hook.get_progressbar_class() with pbar_class(**progress_kwargs) as pbar: ... # do some work pbar.update(1)
在处理前应用特殊行为
- ocrmypdf.pluginspec.validate(pdfinfo: PdfInfo, options: Namespace) None
调用此钩子以允许插件审查 options 和 pdfinfo。
options 包含处理特定文件的“工作订单”。pdfinfo 包含加载和解析后获取的有关输入文件的信息。插件可以修改 options。例如,您可以根据 pdfinfo 中的信息,决定应使用
options.force_ocr = True
处理某种类型的文件。- 引发:
ocrmypdf.exceptions.ExitCodeException – 如果选项或 pdfinfo 不可接受,并且应用程序应以信息性消息和错误代码正常终止。
注意
此钩子将从主进程调用,并且可以在派生子 worker 进程之前修改全局状态。
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
以 canvas 单位的 raster_dpi 分辨率光栅化 PDF 的一页。
图像大小与 raster_dpi 所隐含的整数像素尺寸匹配,即使这些数字不是整数。图像的 DPI 将被 page_dpi 中的值覆盖。
- 参数:
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 所需的字体。
- 返回:
Path – 如果成功,返回 output_file
注意
此钩子将从子进程调用。修改全局状态不会影响主进程或其他子进程。
注意
这是一个 firstresult 钩子。
修改中间图像
- 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 钩子。
- 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 钩子。
- 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。
- 参数:
page – 此页面的上下文。
image_filename – 用于创建 output_pdf 的输入图像的文件名,用于“参考”,如果完全重新创建 output_pdf。
output_pdf – 之前创建的 output_pdf。
- 返回:
output_pdf
注意
此钩子将从子进程调用。修改全局状态不会影响主进程或其他子进程。
注意
这是一个 firstresult 钩子。
OCR 引擎
- ocrmypdf.pluginspec.get_ocr_engine() OcrEngine
返回用于处理此文件的 OcrEngine。
OcrEngine 可以实例化多次,由主进程和子进程共同实例化。
注意
这是一个 firstresult 钩子。
- class ocrmypdf.pluginspec.OcrEngine
表示具有类似于 Tesseract OCR 功能的 OCR 引擎的类。
这可用于为 Tesseract OCR 创建另一个 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 上。
此函数在 worker 线程或 worker 进程中执行。OCRmyPDF 会自动并行化页面的 OCR 处理。OCR 引擎不应引入更多的并行化。
- 参数:
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 页面上。其大小必须与输入图像的尺寸完全相同。
此函数在 worker 线程或 worker 进程中执行。OCRmyPDF 会自动并行化页面的 OCR 处理。OCR 引擎不应引入更多的并行化。
- 参数:
input_file – 要执行 OCR 的页面图像。
output_pdf – 输出 PDF 的预期名称。
output_text – 包含识别文本的文本文件的预期名称。
options – 命令行选项。
- abstract static get_orientation(input_file: Path, options: Namespace) OrientationConfidence
返回图像的方向。
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 强烈假定 PDF/A 生成器具有 Ghostscript 的语义。
OCRmyPDF 将修改元数据,并在生成 PDF/A 后可能对其进行线性化。
- 参数:
pdf_pages – 一个或多个文件名的列表,将合并到 output_file 中。
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
,则继续处理并记录报告。如果钩子无法继续,则无论此设置如何,都应始终引发异常。
- 返回:
Path – 如果成功,钩子应返回
output_file
。
注意
这是一个 firstresult 钩子。
注意
在 15.0.0 版本之前,未提供
context
,而是提供了compression
。插件现在应读取 context 对象以确定是否请求了压缩。
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
钩子来抑制内置优化程序。- 参数:
input_pdf – 输入 PDF,已添加 OCR。
output_pdf – 应由此优化钩子创建的输出 PDF 的请求文件名。
context – 当前上下文。
executor – 可在优化期间使用、用于分配优化任务的已初始化执行器。
linearize – 如果为 True,则 OCRmyPDF 要求
optimize_pdf
返回线性化(也称为快速网页视图)PDF。
- 返回:
Path –
- 如果优化成功,钩子应返回
output_file
。 如果优化未能生成更小的文件,钩子应返回
input_file
。- Sequence[str]: 插件希望报告给用户的任何注释,
特别是未能进一步优化文件的原因。例如,插件可以报告未安装必需的第三方组件,因此未尝试特定优化。
- 如果优化成功,钩子应返回
注意
这是一个 firstresult 钩子。
- ocrmypdf.pluginspec.is_optimization_enabled(context: PdfContext) bool
对于给定的 PdfContext,OCRmyPDF 询问插件优化是否已启用。
优化插件可能已安装并处于活动状态,但可能会被用户设置禁用。
如果此函数返回 False,OCRmyPDF 将执行某些操作来最终确定 PDF。
- 返回:
如果插件的优化已启用,则返回 True。
注意
这是一个 firstresult 钩子。