OCRmyPDF 插件开发实战指南:基于 pluggy 的钩子系统、三种加载方式与 v17 OcrOptions 迁移

OCRmyPDF 插件开发实战指南:基于 pluggy 的钩子系统、三种加载方式与 v17 OcrOptions 迁移 OCRmyPDF 插件开发实战指南基于 pluggy 的钩子系统、三种加载方式与 v17 OcrOptions 迁移【免费下载链接】OCRmyPDFOCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF本文是 OCRmyPDF 官方文档 docs/plugins.md 的深度技术指南。OCRmyPDF 会在扫描版 PDF 中加入可检索的 OCR 文本层而插件机制允许你在固定处理阶段注入自定义行为新增命令行参数、决定某文件是否 OCR、替换 Tesseract 引擎、替换 Ghostscript 栅格化/PDF/A 生成等。读完本文你将掌握脚本插件与打包插件的加载方式、全部钩子hook的调用时机与 firstresult 语义、基于 Pydantic 的插件选项模型以及 v17.0.0 起OcrOptions接口的迁移方法。插件能做什么六类可定制点插件可以在 OCRmyPDF 流水线中的下列兴趣点自定义行为新增命令行参数通过add_options/register_options让插件拥有专属参数参数既可走命令行也可走ocrmypdf.ocr()API覆盖是否对某个文件执行 OCR的决策在读取并解析输入 PDF 后通过validate介入例如依据pdfinfo强制options.mode force对应旧版force_ocr修改即将送入 OCR 的图像通过filter_ocr_image调节 OCR 实际看到的像素与用户最终看到的 PDF 不同修改整页图像后再转成 PDF通过filter_page_image/filter_pdf_page处理整页栅格化结果用行为相似的其他引擎替换 Tesseract实现OcrEngine接口并经get_ocr_engine注册替换 Ghostscript既包括把 PDF 页面栅格化为图像的 rasterizerrasterize_pdf_page也包括 PDF/A 生成器generate_pdfa与优化器optimize_pdf。官方文档在开篇引用了 RFC 2119 的关键词规范MUST / MUST NOT / REQUIRED / SHALL / SHALL NOT / SHOULD / MAY / OPTIONAL表明插件契约中同时存在硬性约束与建议性要求后文会逐一说明。插件技术基础pluggy 与 hookimpl 装饰器OCRmyPDF 的插件系统基于 Python 的pluggy包实现并遵循其约定规范侧所有可用钩子的签名统一定义在 src/ocrmypdf/pluginspec.py通过hookspec pluggy.HookspecMarker(ocrmypdf)标记项目标识符即命名空间ocrmypdf实现侧插件中的每个钩子函数都必须用hookimpl装饰hookimpl从包顶层导出即 src/ocrmypdf/init.py 中的hookimpl _HookimplMarker(ocrmypdf)。最简插件的骨架如下from ocrmypdf import hookimpl hookimpl def add_options(parser): pass需要特别强调的一个设计取舍当前 OCRmyPDF 不会自动检查以 setuptools entrypoint 安装的插件尽管加载代码中保留了对 entrypoint 的读取详见后文加载顺序。这是因为 OCRmyPDF 假设插件对不同的文件可能有不同影响用户未必希望所有插件对所有文件永久生效——因此插件必须由命令行参数--plugin或 API 参数显式激活。这也是下文中脚本插件/打包插件/entrypoint 插件三类加载形态各自定位不同的根源。插件的三种加载方式1. 脚本插件Script plugins临时一次性处理脚本插件就是一个 .py 文件适合非正式或一次性用途比如某一批文件需要特殊处理步骤。通过命令行指定文件路径即可ocrmypdf --plugin ocrmypdf_example_plugin.py input.pdf output.pdf--plugin可以重复出现多次以同时安装多个插件ocrmypdf --plugin plugin_a.py --plugin plugin_b.py input.pdf output.pdf仓库中 misc/example_plugin.py 就是一个可直接运行的完整脚本插件示例它演示了两个典型用法--grayscale-ocr在 OCR 前把图像转成灰度仅影响送入 OCR 的图像不影响最终 PDF--mono-page把输出页全部转成黑白或另存为 JPEG 页。from ocrmypdf import hookimpl hookimpl def add_options(parser): parser.add_argument(--grayscale-ocr, actionstore_true) parser.add_argument(--mono-page, actionstore_true) hookimpl def filter_ocr_image(page, image): if page.options.grayscale_ocr: return image.convert(L) return image hookimpl def filter_page_image(page, image_filename): if page.options.mono_page: with Image.open(image_filename) as im: im im.convert(1) im.save(image_filename) return image_filename同一插件的 API 调用方式是import ocrmypdf ocrmypdf.ocr(input.pdf, output.pdf, plugins[path/to/example_plugin.py], mono_pageTrue)2. 打包插件Packaged plugins面向分发场景如果要分发插件官方文档明确建议将其打包。打包插件安装在与 OCRmyPDF 相同的虚拟环境中用Python 标准模块命名点分模块路径调用ocrmypdf --plugin ocrmypdf_fancypants.pockets.contents input.pdf output.pdf命名约定与 pytest 生态一致发布到 PyPI 的包名建议使用前缀ocrmypdf-如ocrmypdf-exampleplugin对应 pytest 的pytest-covPython 模块名建议使用下划线前缀ocrmypdf_对应pytest_cov同时必须声明插件非官方出品避免用户误认为与 OCRmyPDF 官方同源。3. entrypoint 自动加载插件安装即生效如果插件与 OCRmyPDF 安装在同一虚拟环境还可在pyproject.toml中通过项目 entrypoint 声明让 OCRmyPDF总是自动加载它。OCRmyPDF 使用的 entrypoint 命名空间是ocrmypdf。例如插件名为ocrmypdf-exampleplugin时[project] name ocrmypdf-exampleplugin [project.entry-points.ocrmypdf] exampleplugin exampleplugin.pluginmodule其中键名exampleplugin可任取值exampleplugin.pluginmodule指向实际承载hookimpl函数的模块。加载顺序的源码印证插件管理的实现在 src/ocrmypdf/_plugin_manager.py_setup_plugins()严格按三步注册注册内置插件遍历ocrmypdf.builtin_plugins包tesseract_ocr、ghostscript、optimize、pypdfium、null_ocr、default_filters、concurrency等模块注册 setuptools entrypoint 插件调用self._pm.load_setuptools_entrypoints(ocrmypdf)注册命令行/API 显式指定的插件以.py结尾或Path类型的按文件名用spec_from_file_location动态导入其余按点分模块名importlib.import_module。另外 OcrmypdfPluginManager 支持通过__getstate__/__setstate__进行 pickle 序列化重建——这正是多进程环境下每个 worker 都会重建插件管理器的机制基础见下文。插件运行的硬性约束多进程、无状态与共享状态OCRmyPDF 一般使用多个 worker 进程并行处理页面。每启动一个新 workerPython 都会重新导入所有插件包括早先已导入过的插件。由此得出三条铁律插件钩子实现必须无状态只依赖自己的入参。同一插件会有多个实例同时运行不能指望跨进程共享模块级全局变量如需共享状态可借助传给多个钩子的context对象或经由某个钩子的入参拿到其他钩子准备好的共享引用工作目录纪律插件私有的数据必须写入{options.work_folder}/ocrmypdf-plugin-name子目录。插件 MAY 读写options.work_folder中的文件但要意识到其语义可能随版本变化。关于工作目录的生命周期官方文档有明确承诺OCRmyPDF 处理完一个文件后会删除options.work_folder——除非以--keep-temporary-files调用。也就是说任何需要在完成后保留的中间产物都必须自行转移到别处。此外插件应当同时准备好在 worker 线程或 worker 进程中执行。默认 OCRmyPDF 使用多进程但存在一个半隐藏的线程模式参数便于调试因此插件代码要避免只假设进程模型的写法。钩子Hook全览调用时机与分类完整的钩子规格以 docstring hookspec形式定义在 src/ocrmypdf/pluginspec.py并被官方文档以autofunction/autoclass指令内嵌引用。以下按生命周期与功能归类。firstresult 钩子的特殊语义部分钩子标注为firstresultsrc/ocrmypdf/pluginspec.py 起的多个hookspec(firstresultTrue)。其执行规则是多个插件都实现了该钩子时按安装顺序的逆序调用即后安装者先执行、后安装者胜出依序调用中第一个返回非None的实现将胜出并阻止其余所有实现执行因此你无法用这种方式把多个过滤插件串联成链条——任何需要串联的过滤逻辑应由单个钩子实现内部自行负责。判断一个钩子是否为 firstresult可直接查看插件规格定义处是否带有hookspec(firstresultTrue)。初始化与选项类钩子钩子调用时机与用途类型initialize(plugin_manager)插件首次被加载进 OCRmyPDF 时在主进程调用。主要用途是与其他插件做兼容性检查甚至阻止别的插件例如plugin_manager.set_blocked(ocrmypdf.builtin_plugins.optimize)也可在此检查必需依赖是否缺失若能力取决于参数则改用validate普通add_options(parser)允许插件新增命令行与 API 参数。OCRmyPDF 会把命令行参数转换为 API 参数因此此处新增的参数同时作用于命令行与ocrmypdf.ocr()普通register_options()返回dict[str, type[BaseModel]]把 Pydantic 选项模型按命名空间注册见下节普通check_options(options)校验所有选项。插件可验证自己新增的参数警告通过logging.getLogger(__name__)输出。注意 options 中的一切对象必须可 pickle以便封送到子进程普通validate(pdfinfo, options)在输入 PDF 已加载、解析后调用给插件机会审查options针对该文件的工作单与pdfinfo输入文件信息可修改 options例如依据 pdfinfo 判断某类文件应force_ocr普通上述钩子中initialize/add_options/register_options/check_options/validate均在主进程调用允许在 fork 子进程前修改全局状态文档还说明若选项不可接受钩子应抛出ocrmypdf.exceptions.ExitCodeException让应用以信息丰富的消息与错误码优雅退出。覆盖 OCR 决策与预处理行为validate(pdfinfo, options)如上表是处理前应用特殊行为的钩子。从代码看 validate 的 docstring 举的例子就是基于 pdfinfo 中的信息把某类文件的处理模式设为强制 OCR。自定义命令行参数与选项模型Pydanticadd_options负责把参数加进 argparse parser内置的 Tesseract 插件正是用TesseractOptions.add_arguments_to_parser(parser)批量生成的见 src/ocrmypdf/builtin_plugins/tesseract_ocr.py。自 v17 起插件还可以用Pydantic 定义自己的选项模型从而获得带校验的类型安全选项结构映射到选项模型字段的 CLI 参数通过嵌套命名空间访问选项如options.tesseract.timeout。register_options钩子src/ocrmypdf/pluginspec.py返回命名空间 → Pydantic 模型类的字典。内置插件注册 Tesseract 选项模型的写法可直接照搬hookimpl def register_options(): return {tesseract: TesseractOptions}对应源码 src/ocrmypdf/builtin_plugins/tesseract_ocr.py。而OcrOptions侧会通过register_plugin_models()与动态__getattr__把这些模型挂到嵌套属性上相关实现见 src/ocrmypdf/_options.py当访问options.tesseract这类已注册命名空间时_get_plugin_options()会从扁平字段tesseract_timeout等惰性构造并缓存模型实例。插件选项有两种等价访问方式扁平访问向后兼容options.tesseract_timeout嵌套访问options.tesseract.timeout两种写法返回的值完全一致。内置TesseractOptions模型的字段定义src/ocrmypdf/builtin_plugins/tesseract_ocr.py可作为自定义模型的参考范式用AnnotatedField描述取值范围与默认值例如timeout默认180.0秒、pagesegmode取值 0–13、downsample_above范围 100–32767。执行与进度上报钩子说明类型get_logging_console()返回自定义 logging Handler通常当日志输出与进度条都写sys.stderr时需要用自定义 handler 协调firstresultget_executor(progressbar_class)返回管理并行执行的对象可把 OCRmyPDF 默认并行体系替换为第三方方案例如跑在分布式环境。executor 类比concurrent.futures标准执行器但工作机制不同可被不同批任务复用因为任务上下文都经__call__传入firstresultget_progressbar_class()返回符合ProgressBar协议的进度条类。调用模式pbar_class(**progress_kwargs)进入with块后反复pbar.update(1)firstresult进度条的规格__init__/__enter__/__exit__与成员和Executor__call__的完整契约被官方文档以 autoclass 形式引用可在 src/ocrmypdf/pluginspec.py 与 src/ocrmypdf/_progressbar.py、src/ocrmypdf/_concurrent.py 中查看对应实现。PDF 页面栅格化替换 Ghostscript 光栅器hookspec(firstresultTrue) def rasterize_pdf_page(input_file, output_file, raster_device, raster_dpi, pageno, page_dpi, rotation, filter_vector, stop_on_soft_error, options, use_cropbox) - Path参数含义src/ocrmypdf/pluginspec.pyinput_file/output_file输入的 PDF 与期望的光栅图像输出路径raster_device输出图像类型。源码用GhostscriptRasterDevice枚举约束可选jpeggray、jpeg、pngmono、pngmonod、pnggray、png256、png16mraster_dpi按画布单位栅格化的分辨率即使得到非整数像素尺寸图像也会对齐到整数像素pageno要栅格化的页码从 1 开始page_dpi覆盖输出图像 DPI 标记值rotation顺时针旋转的直角角度filter_vector为 True 时移除矢量图形对象stop_on_soft_error若存在软错误如缺少渲染所需字体图像可继续生成但与原件可能有视觉差异为 True 时应抛出详细异常为 False 则记录日志继续处理。无论该值如何凡无法继续的情况都必须抛异常optionsOCRmyPDF 选项插件可据此判断自己是否应接管如查看options.rasterizerv17.0 引入use_cropbox为 True 时栅格化 CropBox 而非 MediaBox默认 False与 Ghostscript 默认一致返回成功时返回output_file。该钩子在子进程中调用修改全局状态不会影响主进程或其他子进程。修改中间图像的三组过滤钩子三个过滤钩子全部为 firstresult且都在子进程执行filter_ocr_image(page, image) - Image源码——过滤 OCR 将要看到的图像而不是用户看到的 PDF 图像。在--redo-ocr等模式下图像的部分区域可能已被遮罩隐藏。典型用途向 OCR 隐藏内容、用滤镜改善识别条件、把图像调整到符合 OCR 引擎约束。输入可以是彩色/灰度/黑白输出可不同例如引擎不在乎颜色时可转灰度。约束可以改变像素宽高但不得改变宽高比且必须依据新像素尺寸重新计算 DPI否则文本层将与视觉位置错位。内置 Tesseract 引擎就是通过此钩子对超大图像降采样以适配自身限制。filter_page_image(page, image_filename) - Path源码——过滤整页图像后再插入 PDF。注意只有执行了预处理参数或--force-ocr才会生成整页图像否则该钩子不会触发这不是送入 OCR 的图像。不想改动时应原样返回image_filename。约束更严格输出必须保持相同的物理尺寸width*dpi_x与height*dpi_y缩放图像必须用倒数调整 DPI否则页面会被改变大小且 OCR 层错位——OCRmyPDF 不会强制这些约束全靠插件自觉。OCRmyPDF 依据返回的图像格式建页转成 JPEG 就会生成 JPEG 页颜色空间变更也会保留后续优化阶段仍可能改选其他格式。若返回值指向不存在的文件会触发FileNotFoundError返回值应位于与image_filename相同的目录。filter_pdf_page(page, image_filename, output_pdf) - Path源码——把过滤后的整页图像转成单页 PDF仅在 force OCR 这类全部内容栅格化模式下触发image_filename是参考用的输入图像。此处可做聪明事把页面分段成颜色区域或矢量等价物。实现方须保证其产出的 PDF 与 OCR 文本层对齐否则文字错位由插件负责当前必须生成单页 PDF否则流水线失败若意图删除该页也应生成单页空 PDF且返回必须等于output_pdf包装层会强校验见 src/ocrmypdf/_plugin_manager.py。OCR 引擎替换 Tesseractget_ocr_engine(options) - OcrEngine是 firstresult 钩子源码返回本次处理要使用的引擎。引擎实例可能被主进程与子进程多次实例化当装有多套 OCR 引擎插件时各插件应检查options.ocr_engine若自己不是被选中的引擎则返回None让调用者继续尝试下一个插件。OcrEngine是从 src/ocrmypdf/pluginspec.py 抽象基类导出的实现一个可替换引擎需要覆盖以下静态方法方法职责version()返回引擎版本字符串creator_tag(options)返回写入 XMP 元数据与 DocumentInfo 的 Creator 标签应含引擎名与版本、不得含换行OCRmyPDF 会自行在其前附加自己的名字__str__()返回引擎名 版本用于向用户展示通常是错误消息languages(options)返回引擎支持的全部语言集合通常是 3 字母 ISO 639 代码也可以是引擎能理解的任意值get_orientation(input_file, options)返回OrientationConfidence判断页面旋转方向get_deskew(input_file, options)返回去斜角度度基类默认返回0.0可选覆盖generate_hocr(input_file, output_hocr, output_text, options)生成 hOCR 文件与侧车文本文件。OCRmyPDF 从 hOCR 构建纯文本 PDF 并嫁接到输出 PDFgenerate_pdf(input_file, output_pdf, output_text, options)生成纯文本 PDF不可含任何可见内容尺寸须精确等于输入图像将被嫁接到输入页上supports_generate_ocr()返回该引擎是否支持新版generate_ocr()API默认False实现方应覆写为Truegenerate_ocr(input_file, options, page_number0)v17 现代 API返回(OcrElement 树, 纯文本)元组根节点应为OcrClass.PAGEOrientationConfidence是一个NamedTuple源码字段angle表示页面应顺时针旋转的直角0/90/180/2700 表示不旋转confidence表示置信度0 为毫无把握15 为非常确定任意单位。文档特别提醒generate_hocr与generate_pdf执行在 worker 线程或进程中OCRmyPDF 已自动按页并行化引擎自身不应再引入更多并行。PDF/A 生产与优化generate_pdfa(...)源码——firstresult 钩子该 API 强烈假定实现具有 Ghostscript 语义的 PDF/A 生成能力。参数包括pdf_pages一个或多个待合并的文件名、pdfmark给 Ghostscript 用的 PostScript 文件内含 PDF/A 转换细节、output_file、context当前上下文、pdf_version输出 PDF 的最低版本生成方可以自酌提高但不能降低、pdfa_part期望的 PDF/A 合规级别如2b、progressbar_class可空实现 ProgressBar 协议、stop_on_soft_error语义同前。生成后 OCRmyPDF 会修改元数据并可能做 linearization。兼容性说明15.0.0 之前该钩子不提供context而提供compression现在应改从 context 读取压缩请求。optimize_pdf(input_pdf, output_pdf, context, executor, linearize)源码——firstresult 钩子在图像、OCR 与元数据处理之后优化 PDF。要点若输入是 PDF/A优化必须保持其 PDF/A 状态做不到时必须告知用户若实现没能把文件变小应返回input_pdf而非output_pdf返回(Path, Sequence[str])路径为优化成功时的output_file未变小则input_file序列是希望向用户报告的备注例如缺少第三方依赖故未尝试某优化linearize为 True 时要求返回 fast web view线性化PDF新优化器可能需要实现initialize钩子去屏蔽内置优化器plugin_manager.set_blocked(...)。is_optimization_enabled(context) - bool源码——firstresult 钩子针对某个PdfContext回答优化当前是否启用。优化插件即使被安装并激活也可能被用户设置关掉返回False时 OCRmyPDF 会采取特定动作完成 PDF 收尾。v17.0.0 插件接口变更与迁移指南接口变更核心自 OCRmyPDF v17.0.0 起插件钩子收到的是OcrOptions对象而非argparse.Namespace对象。由于鸭子类型兼容多数插件无需改动即可继续工作但插件作者应更新类型标注。1. 更新导入from ocrmypdf._options import OcrOptionsOcrOptions是定义在 src/ocrmypdf/_options.py 的 PydanticBaseModel其 docstring 明确说明它能伪装成 argparse.Namespace提供类型化与校验的同时保持旧行为兼容其内部用extra_attrs承接未知字段extraforbid配合动态属性机制见 src/ocrmypdf/_options.py。2. 更新类型标注# Beforev16 及更早 def check_options(options: argparse.Namespace) - None: ... # Afterv17 def check_options(options: OcrOptions) - None: ...3. 属性访问方式不变以下写法在 v17 中原样可用options.languages options.output_type options.tesseract_timeout4. 移除就地修改的旧模式# Beforev16 模式——不再推荐 def check_options(options): options.some_computed_value compute_value(options) # Afterv17 模式——在使用点计算 def some_function(options): computed compute_value(options) use_computed(computed)迁移逻辑的本质是v17 的选项是带校验的 Pydantic 模型把派生值临时塞进 options 对象做就地修改的做法不再是被推荐的模式应改为在使用点即时计算。v17 的 OcrElement 树绕开 hOCR 的新 OCR 输出格式OCRmyPDF v17 引入OcrElement数据类用引擎无关的格式表示 OCR 输出使插件无需解析 hOCR XML 即可处理识别结果。核心类从包顶层导出src/ocrmypdf/init.pyfrom ocrmypdf import OcrElement, OcrClass, BoundingBox # OcrElement —— 表示任意 OCR 结构单元 page OcrElement( ocr_classOcrClass.PAGE, bboxBoundingBox(0, 0, 612, 792), children[...] ) # BoundingBox —— 轴对齐包围盒left, top, right, bottom bbox BoundingBox(left100, top50, right300, bottom80) # OcrClass —— 元素类型常量 OcrClass.PAGE # ocr_page OcrClass.LINE # ocr_line OcrClass.WORD # ocrx_word OcrClass.PARAGRAPH # ocr_par数据类定义位于 src/ocrmypdf/models/ocr_element.pyBoundingBox为像素坐标、原点在左上与图像/hOCR 一致且在right left或bottom top时抛ValueErrorL17-L54OcrClass复用 hOCR 词汇ocr_page/ocr_carea/ocr_par/ocr_line/ocrx_word 等作为通用语言另有Baseline斜截式基线 yslope*xintercept与FontInfo等辅助结构。遍历树属性定义见 src/ocrmypdf/models/ocr_element.pywords page.words # 页面中所有词返回 list[OcrElement] lines page.lines # 所有行 text page.get_text_recursive() # 递归拼接的全文 for para in page.paragraphs: print(para.get_text_recursive())OCR 引擎插件的新路径实现自定义 OCR 引擎的插件现在可以直接经generate_ocr()方法输出OcrElement树、彻底绕过 hOCRfrom pathlib import Path from ocrmypdf.pluginspec import OcrEngine from ocrmypdf import OcrElement, OcrClass, BoundingBox class MyOcrEngine(OcrEngine): def generate_ocr(self, input_file: Path, options, context) - OcrElement: # 执行 OCR直接返回 OcrElement 树无需生成 hOCR XML return OcrElement( ocr_classOcrClass.PAGE, bboxBoundingBox(0, 0, width, height), dpi300, children[ OcrElement( ocr_classOcrClass.LINE, bboxBoundingBox(100, 50, 500, 80), children[ OcrElement( ocr_classOcrClass.WORD, bboxBoundingBox(100, 50, 200, 80), textHello, ), ] ), ] ) def supports_generate_ocr(self) - bool: return True # 声明本引擎使用 generate_ocr()需要说明的是文档示例与源码 generate_ocr 都要求返回(OcrElement 树, 纯文本字符串)二元组、根为OcrClass.PAGE且该方法执行于 worker 线程/进程——OCRmyPDF 已自动按页并行化引擎不应再自行并行。未实现该方法的引擎保持默认实现流水线会回退到generate_hocr()或generate_pdf()管道正是通过supports_generate_ocr()判断走哪条路径。相比生成 hOCR这套方案更简单能让现代 OCR 引擎与 OCRmyPDF 更自然地集成。参考示例仓库中的现成插件官方文档列出了两类参考测试插件OCRmyPDF 的测试套件内置了多个用于模拟特定测试条件的插件位于 tests/plugins 目录包括tesseract_noop.py、tesseract_crash.py、tesseract_cache.py、tesseract_big_image_error.py、tesseract_debug_rotate.py、tesseract_simulate_oom_killer.py、gs_pdfa_failure.py、gs_raster_failure.py、gs_render_failure.py、gs_render_soft_error.py、gs_raster_soft_error.py、gs_feature_elision.py、stdout_polluter.py等。从命名可推断它们分别模拟 Tesseract 崩溃/空操作/缓存行为、Ghostscript 各类软硬失败、向标准输出注入污染等边界条件是研究如何模拟故障来测试自己的插件的第一手素材生产级插件官方文档提及将 OCRmyPDF 与 Papermerge 文档管理系统集成的生产插件ocrmypdf-papermerge以及作为直接可用且完整实现范本的 OCRmyPDF-EasyOCR 参考实现——如需研究完整插件架构可在各自项目主页查看本文不展开外部链接。小结开发一个插件的最小行动清单对照本文可提炼出落地步骤从from ocrmypdf import hookimpl开始为每个钩子函数加上hookimpl函数签名与 src/ocrmypdf/pluginspec.py 的规格一一对应明确加载形态临时批处理用--plugin xxx.py脚本插件分发用ocrmypdf_前缀模块的打包插件安装即用则配pyproject.toml的[project.entry-points.ocrmypdf]牢记约束钩子无状态、跨 worker 不共享全局、私有数据写入{options.work_folder}/ocrmypdf-plugin-name、必要时用--keep-temporary-files保住工作目录涉及过滤图像时严格遵守宽高比/DPI/物理尺寸三大约束否则文本层错位后果自负关注 v17 接口类型标注改为OcrOptions、属性访问不变、别再做就地修改 options 的旧式操作新引擎可优先走OcrElementgenerate_ocr()的现代路径参考 misc/example_plugin.py 与 tests/plugins 中的现成实现验证行为。这套机制使 OCRmyPDF 不必在核心流水线中内置所有可能性而是把图像预处理、引擎替换、光栅化、PDF/A 生成、优化与执行策略等能力以稳定的钩子契约开放给社区让为特定文件、特定批次、特定业务定制处理成为可组合、可分发、可测试的开发体验。【免费下载链接】OCRmyPDFOCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考