ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PyMuPDF 可选内容(OCG / OCMD)完全指南:在 PDF 中实现图层显示与隐藏的编程控制

PyMuPDF 可选内容(OCG / OCMD)完全指南:在 PDF 中实现图层显示与隐藏的编程控制 图像处理【免费下载链接】PyMuPDFPyMuPDF is a high performance Python library for data extraction, analysis, conversion manipulation of PDF (and other) documents.项目地址https://gitcode.com/gh_mirrors/py/PyMuPDF点击查看免费下载本篇指南以仓库文档 docs/recipes-optional-content.rst 为核心骨架系统讲解 PyMuPDF 对 PDF「可选内容」Optional Content的完整支持从 OCGOptional Content Group与 OC 配置OC Configuration的概念到add_ocg、set_oc、set_ocmd等核心 API 的源码级剖析再到多语言文档、图层开关、条件显隐等实战场景。读完本文你将掌握如何用 PyMuPDF 创建图层、把对象挂接到图层、用逻辑表达式控制图层显隐以及如何在程序里切换整个文档的图层配置。一、什么是 PDF 可选内容OCG、OCMD 与 OC 配置PDF 的「可选内容」是一套标准机制允许文档中的部分内容根据条件显示或隐藏。这些条件既可以由支持该特性的 PDF 阅读器Viewer界面上的开关决定也可以通过程序在运行时设置。其典型应用场景包括CAD 图纸按专业建筑、结构、电气、暖通分层显示地图与 GIS按缩放级别或主题道路、水系、标注切换细节多层排版艺术图层、批注图层、水印图层的叠加与切换多语言文档同一份文档内置多个语言版本用户按需切换详见下文「复杂条件」一节屏幕 / 打印差异化同一内容在屏幕上显示更多细节在打印时隐藏某些层。这套机制依赖三类核心对象对象全称作用OCGOptional Content Group可选内容组定义文档中的「图层」本身是一个带/Type /OCG的 PDF 字典对象拥有名字、Intent、Usage 等属性OCMDOptional Content Membership Dictionary可选内容成员字典把多个 OCG 用逻辑规则/P策略或/VE可见性表达式组合起来形成更复杂的显隐条件OC 配置OC ConfigurationOCG 的更高层组织单位一组 OCG 每个 OCG 期望的初始显隐状态。一个 PDF 可含多个配置切换配置即切换整套显隐方案普通 PDF 对象一段文本、一张图片、一个 Form XObject一旦被「标记」了某个 OCG即在对象字典中写入/OC ocg 0 R它的显隐就由该 OCG或其所属 OCMD的当前状态决定。除默认配置/OCProperties /D外其余 OC 配置都是可选的。关于这套机制的完整背景可参阅 PDF 规范手册ISO 32000中 Optional Content 章节。二、PyMuPDF 对可选内容的支持概览PyMuPDF 对可选内容提供了全生命周期的支持查看、创建、修改、删除 OCG 与 OC 配置维护 OCG 到 PDF 对象的挂接关系以及在程序中切换 OC 配置和单个 OCG 的显隐状态。相关 API 集中在Document类与Page类上主要清单如下API功能源码位置Document.add_ocg()新增一个 OCG自动完成必要的 OCProperties 初始化src/init.py#L4159-L4227Document.set_oc()将 OCG/OCMD 挂接或解除到图片 / Form XObjectsrc/init.py#L7050-L7070Document.get_oc()查询图片 / Form XObject 当前挂接的 OC 对象src/init.py#L4910-L4925Document.get_ocgs()列出文档全部 OCG名称、Intent、Usage、当前显隐状态src/init.py#L4927-L4969Document.set_ocmd()/get_ocmd()创建 / 更新 / 解析 OCMD策略与可见性表达式src/init.py#L7072-L7143、src/init.py#L4971-L5036Document.get_layers()/get_layer()查看 OC 配置列表 / 某个配置的 ON、OFF、RBGroups 内容src/init.py#L4875-L4898、src/init.py#L4853-L4873Document.add_layer()新增一个 OC 配置src/init.py#L4153-L4157Document.set_layer()设置某配置的 /ON、/OFF、/RBGroups、basestatesrc/init.py#L6884-L6947Document.switch_layer()激活切换到某个 OC 配置src/init.py#L7850Document.layer_ui_configs()/set_layer_ui_config()获取 / 设置面向用户的图层开关状态src/init.py#L5711、src/init.py#L6949-L6964Page.get_oc_items()列出页面内容中实际用到的 OCG / OCMDsrc/init.py#L12656这些 API 的可用性在仓库测试 tests/test_optional_content.py 中有完整覆盖验证。三、如何添加可选内容Document.add_ocg源码剖析为 PDF 添加可选内容非常简单——调用Document.add_ocg即可。如果此前该 PDF 完全没有可选内容支持这一步会自动完成必需的初始化例如创建默认 OC 配置。3.1 完整签名与参数说明xref doc.add_ocg(name, config-1, on1, intentNone, usageNone)参数类型默认值说明namestr必填OCG 的名称会写入 PDF 字典的/Name键在阅读器的图层面板中展示configint-1将新 OCG 加入哪个 OC 配置-1表示默认配置/OCProperties /D否则为/Configs数组的序号onint / bool1新 OCG 在该配置中的初始状态非零True加入/ON数组零False加入/OFF数组intentstrNone写入/Intent数组如View、Design为None时默认写入ViewusagestrNone写入/Usage /CreatorInfo /Subtype默认值为Artwork返回值是新建 OCG 的 xref 编号——这是后续把它挂接到 PDF 对象上的钥匙。3.2 底层实现做了什么从 src/init.py#L4159-L4227 的实现可以看到add_ocg在底层完成了一系列 PDF 结构操作创建 OCG 字典对象pdf_add_new_dict新建字典写入/Type /OCG与/Name name写入 Intent构造/Intent数组默认为View也可传入其他意图名写入 Usage 信息构造/Usage /CreatorInfo子字典Creator固定写为PyMuPDFSubtype默认Artwork可传usage覆盖登记到文档的 OCProperties确保/Root /OCProperties /OCGs数组存在并把新 OCG 追加进去——这正是「如果文档原本没有可选内容支持此处自动完成初始化」的机制所在写入当前配置的 Order / ON / OFF在目标配置默认配置/D或/Configs[config]的/Order数组中登记该 OCG根据on的值决定把它放进/ON还是/OFF数组通知 MuPDF 重读 OCG 数据调用ll_pdf_read_ocg让底层状态与 PDF 结构保持同步返回 xref通过pdf_to_num取出对象编号返回给调用方。注意config -1时若文档不存在对应的/Configs数组或序号越界会抛出ValueError对应源码常量MSG_BAD_OC_CONFIG。3.3 基本用法示例import pymupdf # 即 PyMuPDF doc pymupdf.open() # 新建空白 PDF page doc.new_page() # 添加两个图层第二个初始为 OFF ocg_zh doc.add_ocg(中文层, onTrue) ocg_en doc.add_ocg(English Layer, onFalse) # 把文本写入不同图层文本的显隐由对应 OCG 控制 page.insert_text((72, 100), 你好世界, ococg_zh) page.insert_text((72, 200), Hello, World, ococg_en) doc.save(layers.pdf) doc.close()四、把 OCG 挂接到 PDF 对象set_oc与插入时的oc参数4.1 插入新对象时直接指定在Page.insert_image插入图片时可通过oc参数直接指定 OCG或 OCMD的 xref原文档给出的示例为img_xref page.insert_image(rect, filenameimage.file, ocxref)oc参数同样出现在其他内容插入类方法中例如Page.show_pdf_page把一个 PDF 页面作为 Form XObject 嵌入到本页时也支持ocpage.show_pdf_page(r0, src, 0, ococmd0) # 见 tests/test_optional_content.py#L56-L594.2 为已有对象追加 / 更换 / 解除 OCG如果要把一张已存在的图片纳入某个 OCG 的控制需要先查出该图片的 xref记为img_xref再调用doc.set_oc(img_xref, xref) # 把 OCG或 OCMD挂到图片上同一个 OCG 可以同时挂接多个 PDF 对象从而统一控制它们的显隐要解除挂接传0即可doc.set_oc(img_xref, 0) # 移除图片上的 OC 挂接也可以随时调用set_oc换成另一个 OCG/OCMD。4.3 源码视角set_oc的校验与实现从 src/init.py#L7050-L7070 可以看到set_oc的实现要点对象类型校验xref所指向的对象必须是/Subtype为/Image或/Form的对象否则抛出ValueError(bad object type at xref ...)OC 对象类型校验oc 0时其/Type必须是/OCG或/OCMD解除逻辑oc 0且对象已有/OC键时把/OC置为null写入方式最终通过xref_set_key(xref, OC, f{oc} 0 R)在对象字典中写入/OC间接引用。也就是说从 PDF 结构层面看「把对象挂到图层」本质就是给对象字典加一个/OC键而Document.get_oc(xref)src/init.py#L4910-L4925则反向读取该键返回挂接的 OCG/OCMD xref未挂接时返回0。五、复杂可选内容条件OCMD 与可见性表达式单靠一个 OCG 只能表达「开 / 关」两种状态。当需求升级为「A 开且 B 关时才显示」「三选一互斥」这类逻辑关系时就需要引入OCMDOptional Content Membership Dictionary。5.1 两种条件模型策略Policy与可见性表达式VEDocument.set_ocmdsrc/init.py#L7072-L7143支持两种方式来描述条件doc.set_ocmd(xref0, ocgsNone, policyNone, veNone) - int参数类型说明xrefint0表示新建否则为待更新的已有 OCMD 的 xrefocgslistOCG xref 列表与policy搭配使用policystr取值AllOn、AllOff、AnyOn、AnyOff大小写不敏感对应 PDF 的/P键velist可见性表达式visibility expression对应 PDF 的/VE键使用时忽略ocgs/policyPolicy 模型例如ocgs[ocg1, ocg2], policyAllOn表示「ocg1 与 ocg2 都 ON 时才可见」AnyOff表示「任一 OFF 即不可见」VE 模型用嵌套列表表达布尔表达式支持三个操作符and、or、not其中not只能有两个元素[not, 子表达式]。源码ve_maker会递归地把 Python 列表转换成 PDF 的/VE数组语法并校验每个引用的 OCG xref 必须真实存在于文档中all_ocgs集合检查非法的操作符或格式会抛出ValueError。例如「ocg1 开且 ocg2、ocg3 全关」可写作ve [and, ocg1, [not, [or, ocg2, ocg3]]] ocmd doc.set_ocmd(veve)5.2 多语言文档经典实战场景原文档特别提到一个典型场景——多语言文档为每种语言建一个 OCG用户或程序按需切换语言。其基本做法是每种语言一个 OCG为「互斥」语义再配上 OCMD。下面是一个「三种语言互斥切换」的骨架import pymupdf doc pymupdf.open() page doc.new_page() # 每种语言一个 OCG默认只开中文 ocg_zh doc.add_ocg(中文, onTrue) ocg_en doc.add_ocg(English, onFalse) ocg_fr doc.add_ocg(Français, onFalse) # 互斥表达式显示中文 ⇔ 中文开且英、法关 ocmd_zh doc.set_ocmd(ve[and, ocg_zh, [not, [or, ocg_en, ocg_fr]]]) ocmd_en doc.set_ocmd(ve[and, ocg_en, [not, [or, ocg_zh, ocg_fr]]]) ocmd_fr doc.set_ocmd(ve[and, ocg_fr, [not, [or, ocg_zh, ocg_en]]]) # 同一位置放三份文本分别挂到对应的 OCMD r pymupdf.Rect(72, 100, 500, 160) page.insert_textbox(r, 欢迎使用 PyMuPDF, ococmd_zh) page.insert_textbox(r, Welcome to PyMuPDF, ococmd_en) page.insert_textbox(r, Bienvenue à PyMuPDF, ococmd_fr) doc.save(multilang.pdf) doc.close()仓库测试 tests/test_optional_content.py#L29-L65test_oc2给出了一个更完整的四象限实例把源 PDF 的 4 页分别show_pdf_page到新页面的 4 个区域每个区域挂接一个互斥 OCMD[and, ocg_i, [not, [or, ...其余三个...]]]保证任意时刻只有一张源页可见。它还演示了配套的读取手段xobj_ocmds [doc.get_oc(item[0]) for item in page.get_xobjects() if item[1] ! 0] assert set(ocmds) set(xobj_ocmds) # 验证挂接成功 assert set((ocg0, ocg1, ocg2, ocg3)) set(tuple(doc.get_ocgs().keys())) doc.get_ocmd(ocmd0) # 读取 OCMD 定义 page.get_oc_items() # 页面实际使用的 OC 对象5.3 反向解析get_ocmdDocument.get_ocmd(xref)src/init.py#L4971-L5036负责把 PDF 中的 OCMD 对象还原为 Python 字典。其实现通过字符串解析 PDF 对象文本识别/OCGs数组、/P策略字符串、/VE可见性表达式数组并把/VE中的 PDF 数组/And、/Not、/Or转换成 Python 嵌套列表。返回结构为{xref: xref, ocgs: [...], policy: ..., ve: [...]}该返回结果可直接作为set_ocmd的输入复用实现「读取 → 修改 → 回写」的闭环。六、OC 配置管理图层切换与状态维护当 OCG 数量增多后把它们组织进不同的OC 配置里就可以一键切换整套显隐方案。PyMuPDF 的相关 API 围绕「默认配置/D」与「/Configs数组」展开。6.1 查看get_ocgs、get_layers、get_layerdoc.get_ocgs() # - {xref: {name: str, intent: [str], on: bool, usage: str}} doc.get_layers() # - [{number: int, name: str, creator: str}, ...] doc.get_layer(config-1) # 某配置的 {ON: [...], OFF: [...], RBGroups: [...]}get_ocgssrc/init.py#L4927-L4969遍历/Root /OCProperties /OCGs数组逐个读取/Name、/Intent、/Usage /CreatorInfo /Subtype并调用底层pdf_is_ocg_hidden判断当前显隐状态写入on键。注意它只统计 /OCGs 数组中登记的组——文档若未初始化可选内容则返回空字典。get_layerssrc/init.py#L4875-L4898返回各 OC 配置的编号、名称与创建者。get_layersrc/init.py#L4853-L4873返回某个配置中/ON、/OFF、/RBGroups互斥组三个数组的内容config-1指默认配置。6.2 修改add_layer与set_layerdoc.add_layer(name, creatorNone, onNone) # 新增 OC 配置 doc.set_layer(config, basestateNone, onNone, offNone, rbgroupsNone, lockedNone)set_layersrc/init.py#L6884-L6947的每个参数都有严格校验basestateON、OFF或Unchanged大小写不敏感决定未在 ON/OFF 中列出的 OCG 的默认状态on/off/lockedOCG xref 列表必须是文档中真实存在的 OCG否则抛ValueErrorrbgroups互斥组列表形如[[ocg1, ocg2], [ocg3, ocg4]]每组内至多一个 OCG 可以同时为 ONPDF 的/RBGroups语义写入完成后同样调用ll_pdf_read_ocg同步底层状态。6.3 切换switch_layerdoc.switch_layer(config, as_default0) # 激活第 config 个配置switch_layersrc/init.py#L7850把指定配置设为当前生效配置。如果传as_default1还会把该配置写为文档的默认配置。6.4 面向用户界面的开关layer_ui_configs与set_layer_ui_config很多 PDF 阅读器会在侧栏提供「图层」面板layer_ui_configs()src/init.py#L5711返回这些可由用户操作的开关项列表每项含编号与显示文本。而set_layer_ui_config(number, action)src/init.py#L6949-L6964可编程地模拟用户操作action0选中pdf_select_layer_config_ui即打开该层action1切换pdf_toggle_layer_config_uiaction2取消选中pdf_deselect_layer_config_ui即关闭该层。number也支持直接传图层名称字符串内部会先经layer_ui_configs()按text匹配出编号。测试 tests/test_optional_content.py#L67-L74test_3143还验证了非 ASCII 图层名如中文名在layer_ui_configs、page.get_drawings()与page.get_bboxlog(layersTrue)三处返回一致说明图层名可安全使用多语言文本。七、页面级查询Page.get_oc_items除文档级 API 外Page.get_oc_items()src/init.py#L12656可返回该页面内容流中实际引用到的OCG 与 OCMD 列表。它基于页面内容流的解析结果而不是遍历全文档因此在「某页到底用了哪些图层」的场景下非常高效也常用来验证挂接是否真正写入了页面内容。八、综合实战从零构建一个带图层开关的 PDF把以上 API 串起来一个完整的「建图层 → 挂对象 → 配互斥 → 切换配置 → 验证」工作流如下import pymupdf doc pymupdf.open() page doc.new_page() # 1) 创建图层 ocg_a doc.add_ocg(背景, onTrue) ocg_b doc.add_ocg(前景, onTrue) ocg_c doc.add_ocg(批注, onFalse) # 2) 把对象挂到图层插入时指定 oc page.draw_rect(pymupdf.Rect(72, 72, 300, 400), color(0.9, 0.9, 0.9), ococg_a) page.insert_text((100, 100), Foreground Text, ococg_b) page.insert_text((100, 500), Comment Here, ococg_c, fontsize8, color(1, 0, 0)) # 3) 新增一个「只显示背景」的配置并切换过去 doc.add_layer(仅背景) doc.set_layer(config1, basestateOFF, on[ocg_a]) doc.switch_layer(1) # 4) 验证当前配置内容 print(doc.get_layer(1)) # 应包含 ON[ocg_a] print(doc.get_ocgs()) print(page.get_oc_items()) doc.save(ocg_demo.pdf) doc.close()运行后可用支持图层面板的 PDF 阅读器打开ocg_demo.pdf在图层面板中勾选 / 取消各图层验证显隐效果。九、测试与进一步阅读可选内容功能的自动化测试集中在 tests/test_optional_content.py覆盖任意 API 组合调用test_oc1、OCMD 互斥四象限嵌入与挂接校验test_oc2、非 ASCII 图层名一致性test_3143、图层 表单控件联动的多语言切换骨架test_3180。本主题的权威原始文档即 docs/recipes-optional-content.rst仓库还提供了名为optional-content.ipynb的 Jupyter Notebook 演示位于 PyMuPDF-Utilities 配套仓库中可交互式体验多语言 / 多条件显隐的完整流程适合在此基础上扩展自己的复杂需求。若需深入了解 PDF 层面的对象结构操作如/OC键、OCProperties、/VE数组可结合 src/init.py 中上述各 API 的源码阅读它们与 ISO 32000 的可选内容规范一一对应。十、结语PyMuPDF 把 PDF 可选内容的「建、挂、查、切、删」全流程封装成了十余个高可读性的 Python APIadd_ocg一步完成图层创建与默认配置初始化set_oc/oc参数把任意图片、文本、Form XObject 纳入图层控制set_ocmd用policy或嵌套ve表达式表达任意布尔条件set_layer/switch_layer/set_layer_ui_config则覆盖了配置级与 UI 级的状态管理。结合仓库源码与测试用例你完全可以在 CAD 分层、多语言文档、地图分级显示等真实业务中直接落地这套能力。赞分享图像处理【免费下载链接】PyMuPDFPyMuPDF is a high performance Python library for data extraction, analysis, conversion manipulation of PDF (and other) documents.项目地址https://gitcode.com/gh_mirrors/py/PyMuPDF点击查看免费下载相关推荐Epoch图层管理如何控制图表的显示与隐藏Epoch图层管理如何控制图表的显示与隐藏 Epoch是一个功能强大的图表库专为应用程序开发者和可视化设计师设计。在数据可视化项目中 图层管理 功能让您能数据可视化前端Ant Design Tooltip 箭头完全控制指南显示、隐藏与居中定位的实现原理Ant Design Tooltip 箭头完全控制指南显示、隐藏与居中定位的实现原理 本文围绕 Ant Design Tooltip 组件中 arrow 属性前端UI组件设计系统Tippecanoe属性操作指南合并、计算和转换地理特征属性Tippecanoe属性操作指南合并、计算和转换地理特征属性 Tippecanoe 是一款强大的开源工具能够从大量 GeoJSON 特征中构建矢量瓦片集广开发工具上一篇Cataclysm-DDA 魔法平衡指南Magiclysm 法术层级、合法性分级与施法专精体系深度解析下一篇Mi-Create打造个性化小米手表表盘的终极免费工具指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表