- 图形学
- 图像处理
【免费下载链接】mupdf
mupdf mirror
本文基于 MuPDF 官方参考文档 PDFPage.rst 展开,系统讲解 MuPDF JavaScript API 中PDFPage类型的全部静态常量与实例方法,包括页面加载、批注增删改、页面盒子设置、toPixmap页面渲染、Redaction(去标识化/涂黑)两阶段流程、页面内容流处理与坐标变换等。读完后,你可以在mutool run的 JS 环境中独立完成页面级读取、编辑、渲染与敏感信息移除等完整工作流,并能对照 source/tools/murun.c 中的 FFI 绑定源码验证每个方法的底层行为。
一、PDFPage 在 MuPDF JS API 中的定位
PDFPage是 MuPDF JavaScript 绑定中代表 PDF 文档中单个页面的对象类型。根据 PDFPage.rst 的说明:
PDFPage继承自通用Page类型(参见 Page.rst),因此所有Page接口(如getSize、toDisplayList、run等)同样可用,PDFPage在其上叠加了 PDF 专属能力;- 该类型没有可直接调用的构造函数。
PDFPage实例只能通过调用PDFDocument的Document.prototype.loadPage方法获得,且文档中特别注明这一点针对PDFDocument; - 在 C 源码层面,所有
PDFPage方法都通过 FFI 形式注册到 mujs 解释器中。可以在 source/tools/murun.c 看到完整的注册表:
js_getregistry(J, "fz_page"); js_newobjectx(J); { jsB_propfun(J, "PDFPage.getObject", ffi_PDFPage_getObject, 0); jsB_propfun(J, "PDFPage.getWidgets", ffi_PDFPage_getWidgets, 0); jsB_propfun(J, "PDFPage.getAnnotations", ffi_PDFPage_getAnnotations, 0); jsB_propfun(J, "PDFPage.createAnnotation", ffi_PDFPage_createAnnotation, 1); jsB_propfun(J, "PDFPage.deleteAnnotation", ffi_PDFPage_deleteAnnotation, 1); jsB_propfun(J, "PDFPage.createSignature", ffi_PDFPage_createSignature, 0); jsB_propfun(J, "PDFPage.update", ffi_PDFPage_update, 0); jsB_propfun(J, "PDFPage.applyRedactions", ffi_PDFPage_applyRedactions, 4); jsB_propfun(J, "PDFPage.process", ffi_PDFPage_process, 1); jsB_propfun(J, "PDFPage.toPixmap", ffi_PDFPage_toPixmap, 6); jsB_propfun(J, "PDFPage.getTransform", ffi_PDFPage_getTransform, 0); jsB_propfun(J, "PDFPage.setPageBox", ffi_PDFPage_setPageBox, 2); jsB_propfun(J, "PDFPage.countAssociatedFiles", ffi_PDFPage_countAssociatedFiles, 0); jsB_propfun(J, "PDFPage.associatedFile", ffi_PDFPage_associatedFile, 1); jsB_propfun(J, "PDFPage.clip", ffi_PDFPage_clip, 1); } js_setregistry(J, "pdf_page");这个注册表同时说明了各方法的实参个数,是理解"哪些参数可省略"的可靠依据。加载页面的入口实现在 source/tools/murun.c 的ffi_Document_loadPage中,内部调用fz_load_page,页码从 0 开始。一个典型的获取实例的写法是:
var doc = mupdf.Document.openDocument("document.pdf", null) var page = doc.loadPage(0) // 返回 PDFPage 实例二、读取页面结构与对象:getObject、getAnnotations、getWidgets
getObject
PDFPage.prototype.getObject()返回该页面对应的底层PDFObject(即 PDF 中 Page 字典对象),是页面级"逃生舱口"——拿到它之后即可通过PDFObject字典接口直接读写任意键值,参见 PDFObject.rst。
var obj = page.getObject()源码实现见 source/tools/murun.c:ffi_PDFPage_getObject直接取出pdf_page结构体中的page->obj并以pdf_keep_obj保留引用,因此返回的对象与内部页面保持同一生命周期。
getAnnotations
返回页面上所有批注组成的数组(元素类型为PDFAnnotation)。实现(source/tools/murun.c)通过pdf_first_annot/pdf_next_annot遍历页面对象的/Annots链表,逐个构造PDFAnnotationuserdata 压入 JS 数组:
var annots = page.getAnnotations()getWidgets
与上类似,但只返回表单控件(widget 是批注的一个子类)。实现(source/tools/murun.c)调用pdf_first_widget/pdf_next_widget,返回的数组元素类型为PDFWidget(参见 PDFWidget.rst):
var widgets = page.getWidgets()批注与控件的完整属性、坐标、内容、矩形等操作,可继续查阅 PDFAnnotation.rst。
三、批注的创建、删除与更新
createAnnotation(type)
创建指定类型的空白批注,type为 PDF 规范的批注类型字符串(如"Text"、"Highlight"、"Redaction"等),返回PDFAnnotation:
var annot = page.createAnnotation("Text")从源码看(source/tools/murun.c),实现先用pdf_annot_type_from_string把字符串解析为枚举(解析失败时得到PDF_ANNOT_UNKNOWN),再调用 C 层的pdf_create_annot(ctx, page, type)。也就是说,创建批注这一步同时完成了"构造批注对象 + 挂到页面 /Annots"两件事。
deleteAnnotation(annot)
从页面删除一个PDFAnnotation,内部调用pdf_delete_annot(source/tools/murun.c):
var annots = page.getAnnotations() page.deleteAnnotation(annots[0])注意原参考文档示例中写作page.delete(annots[0]),这是笔误,实际注册的方法名是deleteAnnotation(见上文注册表)。
update()
遍历页面上所有批注并执行更新,返回值是 boolean:只要有任何一个批注因事件、JavaScript 动作或人工编辑而发生了变化,就返回true,提示宿主程序需要重新渲染该页。底层对应pdf_update_page(source/tools/murun.c):
var updated = page.update()在交互式编辑器中,这通常与PDFAnnotation的edit/事件回调配合使用:编辑结束 -> 调page.update()-> 根据返回值决定是否触发重绘。
四、设置页面盒子:setPageBox(box, rect)
PDFPage.prototype.setPageBox(box, rect)用于修改页面某一种 box 的尺寸。box是页面盒子类型名,rect是新的期望尺寸(Rect):
page.setPageBox("ArtBox", [10, 10, 585, 832])合法的 box 名称取值。从实现看(source/tools/murun.c 调用fz_box_type_from_string),名称解析在 source/fitz/document.c 中完成,大小写不敏感,仅支持以下五种:
| box 名称 | 含义 |
|---|---|
MediaBox | 页面实际介质(纸张/画布)大小 |
CropBox | 显示、打印、绘图所用的默认可见区域 |
BleedBox | 印刷出血区域 |
TrimBox | 成品裁切线区域 |
ArtBox | 艺术内容区域(如封面设计区) |
源码中若名称无法识别会得到FZ_UNKNOWN_BOX,FFI 层随即抛出"invalid page box name"错误,因此传入非法名称不会静默成功。确认合法后再写入 C 层pdf_set_page_box。
五、渲染页面为像素图:toPixmap(matrix, colorspace, alpha, renderExtra, usage, box)
toPixmap将页面渲染成Pixmap位图,是 JS 侧生成缩略图、做预处理(deskew、OCR 前处理等)的核心入口:
var pixmap = page.toPixmap( mupdf.Matrix.identity, // 变换矩阵 mupdf.ColorSpace.DeviceRGB, // 目标颜色空间 true, // 是否带 alpha 通道 false, // 是否渲染批注与控件 "View", // 用途:View 或 Print "CropBox" // 使用的页面盒子 )参数完整说明(含默认值):
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
matrix | Matrix | 从页面坐标到输出像素的变换矩阵 | 无 |
colorspace | ColorSpace | 输出像素的颜色空间,如DeviceRGB、DeviceCMYK | 无 |
alpha | boolean | 输出的 Pixmap 是否包含 alpha 通道 | 无 |
renderExtra | boolean | 是否渲染批注与表单控件 | true |
usage | string | 输出目标场景,"View"或"Print" | "View" |
box | string | 使用的页面盒子名 | "CropBox" |
从源码实现(source/tools/murun.c)可以进一步看清两个细节:
- 默认值:
renderExtra未传时按1(true)处理,usage未传时按"View",box未传时按FZ_CROP_BOX——与文档声明一致; - 渲染路径分流:
renderExtra为true时调用pdf_new_pixmap_from_page_with_usage(页面内容 + 批注 + 控件一起渲染);为false时改走pdf_new_pixmap_from_page_contents_with_usage(只渲染页面内容流,跳过批注层)。这两个 C 函数声明于 include/mupdf/pdf/page.h。
usage参数对应 PDF 的 Usage 特性:设为"Print"时,仅当批注带有Print标志时才会出现在输出中,这与mudraw等工具的--print行为一致。
六、敏感信息移除:applyRedactions 两阶段流程
静态常量
PDFPage带有一组用于 Redaction(去标识化,俗称"涂黑")的静态常量(文档标注为 MuPDF.js 平台可用,WASM 绑定 platform/wasm/lib/mupdf.ts 中也定义了同样的取值)。各常量与 C 层枚举一一对应,枚举定义及权威注释在 include/mupdf/pdf/page.h:
| 常量 | 值 | 语义 |
|---|---|---|
PDFPage.REDACT_IMAGE_NONE | 0 | 完全不处理图片 |
PDFPage.REDACT_IMAGE_REMOVE | 1 | 图片与涂黑区域相交(即使被裁剪)则整张移除 |
PDFPage.REDACT_IMAGE_PIXELS | 2 | 仅把侵入涂黑区域的像素替换为黑色 |
PDFPage.REDACT_IMAGE_UNLESS_INVISIBLE | 3 | 仅在图片可见部分侵入涂黑区域时整张移除;源码注释提示该方式在去标识化阶段可能"泄漏"不可见信息,需谨慎使用 |
PDFPage.REDACT_LINE_ART_NONE | 0 | 不处理线条图形(line art) |
PDFPage.REDACT_LINE_ART_REMOVE_IF_COVERED | 1 | 线条被涂黑区域覆盖时移除 |
PDFPage.REDACT_LINE_ART_REMOVE_IF_TOUCHED | 2 | 线条只要与涂黑区域相交即移除 |
PDFPage.REDACT_TEXT_REMOVE | 0 | 移除与涂黑区域相交的文字(默认,安全行为) |
PDFPage.REDACT_TEXT_NONE | 1 | 不移除任何文字——源码明确警告这样做是不安全的 |
此外 C 枚举中还定义了PDF_REDACT_TEXT_REMOVE_INVISIBLE(移除重叠区域中的不可见文字层,常用于清理 OCR 残留层),但 JS 文档当前只列出上述 9 个常量,使用时以文档列出的为准。
applyRedactions(blackBoxes, imageMethod, lineArtMethod, textMethod)
方法签名与默认值:
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
blackBoxes | boolean | 是否在涂黑位置保留黑底框 | — |
imageMethod | number | 图片处理方式 | REDACT_IMAGE_PIXELS |
lineArtMethod | number | 线条处理方式 | REDACT_LINE_ART_REMOVE_IF_COVERED |
textMethod | number | 文字处理方式 | REDACT_TEXT_REMOVE |
Redaction 是一种特殊批注,用于永久删除PDF 内容。整个流程是不可逆的两步操作:先创建Redaction批注圈定范围,再调用applyRedactions真正执行删除:
var annot1 = page.createAnnotation("Redaction") annot1.setRect([0, 0, 500, 100]) var annot2 = page.createAnnotation("Redaction") annot2.setRect([0, 600, 500, 700]) page.applyRedactions(true, mupdf.PDFPage.REDACT_IMAGE_NONE)上例中只显式传了两个参数,其余按默认值生效。FFI 实现(source/tools/murun.c)构造pdf_redact_options结构体(定义见 include/mupdf/pdf/page.h),对未定义的参数保留 C 侧默认值{ 1, PDF_REDACT_IMAGE_PIXELS, 0, 0 },最终调用pdf_redact_page执行全页去标识化。由于applyRedactions会重写页面内容流与资源,执行后应保存文档(PDFDocument.save/Document.saveIncremental)并重新加载验证,原始内容无法从文件中恢复。
七、坐标体系与内容流处理
getTransform()
返回从 MuPDF 页面空间到 PDF 用户空间的变换。两种坐标系的关键区别(可对照 coordinate-system.md 深入理解):
- MuPDF 页面空间:原点位于页面左上角,y 轴向下增长,单位固定为 72 dpi 下的点;
- PDF 用户空间:原点位置任意(由页面盒子决定),y 轴向上增长,单位受
UserUnit影响。
var transform = pdfPage.getTransform()需要说明的一点:参考文档中该方法的返回类型标注为Rect,但从源码看(source/tools/murun.c),实现是调用pdf_page_transform得到fz_matrix后通过ffi_pushmatrix压栈,实际返回的是一个Matrix对象。在编写坐标换算逻辑时请以矩阵形式处理为准。
process(processor)
|only_mutool|标注表示该方法仅在mutool run环境可用。它遍历页面内容流(contents stream),把遇到的每个操作符回调给传入的PDFProcessor对象,可用于审计、转换或统计页面绘制指令:
pdfPage.process(processor)从实现看(source/tools/murun.c),FFI 层先取pdf_page_resources与pdf_page_contents,再用pdf_process_contents驱动 C 层解释器逐条执行页面指令,PDFProcessor的回调对象(new_js_processor创建的包装)在每个操作符上被调用。PDFProcessor接口见 PDFProcessor.rst。
八、仅 mutool 可用的其他页面能力
以下三个方法在参考文档中带有|only_mutool|标记,表示只在mutool run的完整 JS 环境里注册可用:
createSignature(name)
创建一个以name作为字段标签(field label)的新签名控件,返回PDFWidget。底层调用pdf_create_signature_widget(source/tools/murun.c):
var signatureWidget = pdfPage.createSignature("test")countAssociatedFiles() / associatedFile(n)
用于处理 PDF 的 Associated Files(关联文件,常见于 Office 导出的文档中嵌入的原始部件)。注意文档特别强调:countAssociatedFiles()返回的是本页关联的文件数,而不是整份文档的总数(文档级计数请使用PDFDocument.countAssociatedFiles,参见 PDFDocument.rst)。
var count = pdfPage.countAssociatedFiles() var obj = pdfPage.associatedFile(0) // 0 <= n < count,越界返回 null实现分别对应 C 层的pdf_count_page_associated_files与pdf_page_associated_file(source/tools/murun.c),后者返回表示第n个关联文件的 file specification 对象,越界时返回null。
clip(rect)
把页面裁剪到给定矩形。注意clip是永久性的页面编辑,不是渲染时的一次性裁剪,执行后需要保存文档才能持久生效:
pdfPage.clip([0, 0, 200, 300])实现调用 C 层pdf_clip_page(source/tools/murun.c)。
九、方法速查表与源码对照
下表汇总 PDFPage.rst 定义的全部实例方法及其实现在murun.c中的位置,方便按图索骥:
| 方法 | 作用 | FFI 实现位置 |
|---|---|---|
getObject() | 取底层PDFObject | murun.c#L9630 |
getAnnotations() | 全部批注数组 | murun.c#L9663 |
getWidgets() | 全部表单控件数组 | murun.c#L9637 |
setPageBox(box, rect) | 修改页面盒子 | murun.c#L9813 |
createAnnotation(type) | 创建空白批注 | murun.c#L9689 |
deleteAnnotation(annot) | 删除批注 | murun.c#L9704 |
update() | 更新全部批注,返回是否变化 | murun.c#L9730 |
toPixmap(matrix, cs, alpha, extra, usage, box) | 渲染页面为Pixmap | murun.c#L9775 |
applyRedactions(...) | 执行去标识化(不可逆) | murun.c#L9742 |
process(processor) | 遍历内容流回调PDFProcessor | murun.c#L9757 |
getTransform() | 页面空间到用户空间的变换 | murun.c#L9799 |
createSignature(name) | 创建签名控件(仅 mutool) | murun.c#L9715 |
countAssociatedFiles() | 本页关联文件数(仅 mutool) | murun.c#L9829 |
associatedFile(n) | 取第 n 个关联文件对象(仅 mutool) | murun.c#L9841 |
clip(rect) | 永久裁剪页面(仅 mutool) | murun.c#L9854 |
十、实践建议与注意事项
- 获取实例的唯一入口:不要尝试
new PDFPage(),始终通过pdfDocument.loadPage(n)获取;页码从 0 起算。 - 修改后必须保存:
setPageBox、createAnnotation、deleteAnnotation、clip、applyRedactions修改的都是文档对象树中的状态,执行Document.save(或增量保存)后变更才会落盘。 - Redaction 的不可逆性:
applyRedactions会真正从内容流中删除文字、改写图片与线条,测试时应先在副本文件上执行;若需要"看起来涂黑但保留原文"的效果,请改用 Black 填充的批注而不是 Redaction。 - 平台差异:标注
|only_mutool|的方法在mutool run中可用;Redaction 常量文档标注为 MuPDF.js 专属;在 Java/WASM 等其他绑定中对应的 API 面可能不同(例如 Java 侧见 platform/java/src/com/artifex/mupdf/fitz/PDFPage.java)。 - 坐标系换算:凡涉及批注矩形、裁剪矩形与页面像素的换算,先用
getTransform()拿到页面空间到用户空间的矩阵(按Matrix处理),再结合toPixmap的matrix参数统一坐标,避免混用两种坐标系。
- 图形学
- 图像处理
【免费下载链接】mupdf
mupdf mirror
相关推荐
MuPDF JavaScript 绑定 PDFPage 详解:PDF 页面的标注、批量编辑、永久涂黑与页面框操作
MuPDF JavaScript 绑定 PDFPage 详解:PDF 页面的标注、批量编辑、永久涂黑与页面框操作 本篇技术指南以本仓库内 MuPDF 子项目(
桌面应用文档MuPDF 的 DisplayListDevice 完整指南:用 JavaScript 录制与重放页面渲染(基于 SumatraPDF 内置 MuPDF)
MuPDF 的 DisplayListDevice 完整指南:用 JavaScript 录制与重放页面渲染(基于 SumatraPDF 内置 MuPDF) Di
桌面应用文档MuPDF JavaScript 绑定 ColorSpace 全解析:颜色空间类型、ICC 配置文件与 Pixmap 渲染实战
MuPDF JavaScript 绑定 ColorSpace 全解析:颜色空间类型、ICC 配置文件与 Pixmap 渲染实战 导读 ColorSpace 是
图形学图像处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考