MuPDF TreeArchive 详解:构建内存中的命名缓冲区归档(JS API 与 C 实现剖析)

发布时间:2026/10/14 17:40:40
MuPDF TreeArchive 详解:构建内存中的命名缓冲区归档(JS API 与 C 实现剖析) 图形学图像处理【免费下载链接】mupdfmupdf mirror项目地址https://gitcode.com/gh_mirrors/mu/mupdf点击查看免费下载本文基于 MuPDF 官方参考文档 TreeArchive.rst系统讲解TreeArchive这一 JavaScript 类型如何在mutool脚本中创建一个纯内存的“命名缓冲区归档”向其中写入Buffer数据并像普通归档一样按名读取同时结合仓库 C 源码剖析其底层实现——AA-tree 索引、缓冲区的引用计数语义以及它在 Java 绑定与 MOBI 文档解析中的实际应用。1. 什么是 TreeArchiveMuPDF 的归档Archive机制把“包含多个命名文件的容器”抽象成统一接口Archive.rst 定义的mupdf.Archive可以打开 zip、tar 文件或目录并提供getFormat()、countEntries()、listEntry(idx)、hasEntry(name)、readEntry(name)等读侧方法。TreeArchive则是这一族的内存实现它不依赖磁盘上的 zip/tar 文件而是用一棵以条目名为键、fz_buffer为值的树AA-tree在内存中组织数据。构造一个空归档后脚本可以用add(name, buffer)不断向其中追加命名条目之后即可用 Archive 家族的标准读方法访问这些条目。其典型用途是在内存中临时组装一组文件如测试数据、字体子集、表单附件再交给需要“按名读文件”的 MuPDF 接口作为归档家族的“可写内存后端”与只读的 zip/tar 归档在读接口上完全兼容。2. JavaScript API 完整参考以下接口在mutool的 JavaScript 环境中可用文档中标注|only_mutool|即仅mutool run提供的 MuJS 环境包含该类型WASM 构建中未导出TreeArchive。2.1 构造函数new mupdf.TreeArchive()创建一个空的树归档不需要任何参数var treeArchive new mupdf.TreeArchive()2.2 实例方法TreeArchive.prototype.add(name, buffer)向归档中添加一个命名条目。参数如下参数类型说明namestring归档条目的名称键后续读取时按此名精确匹配bufferBuffer \| ArrayBuffer \| Uint8Array \| string要随条目存储的数据mupdf.Buffer、ArrayBuffer、Uint8Array乃至普通字符串都会被转换为 MuPDF 缓冲区示例出自官方文档可复制到mutool run中运行var buf new mupdf.Buffer() buf.writeLine(hello world!) var archive new mupdf.TreeArchive() archive.add(file2.txt, buf) print(archive.hasEntry(file2.txt)) // 输出 true注意两点实现语义与 include/mupdf/fitz/archive.h 中fz_tree_archive_add_buffer的注释一致不转移所有权归档会对传入的 buffer 取一个新引用fz_keep_buffer调用方持有的 buffer 对象依然独立存活可以复用给其他条目条目名会被规范化C 层在每次按键查找前先执行fz_cleanname见 source/fitz/archive.c把路径分隔符统一清理为/因此跨平台脚本中写入dir\\file.txt与dir/file.txt最终会命中同一规范化路径规则建议脚本统一使用/作为分隔符。2.3 从 Archive 家族继承的读侧方法TreeArchive在 C 层是fz_archive的特化实例因此在mutool环境中与 Archive.rst 相同的一组读方法都可以直接作用于它var archive new mupdf.TreeArchive() var buf new mupdf.Buffer() buf.writeLine(hello world!) archive.add(file2.txt, buf) print(archive.getFormat()) // tree print(archive.hasEntry(file2.txt)) // true var content archive.readEntry(file2.txt) print(content.toString())行为边界由 C 实现直接决定见第 3 节getFormat()返回字符串treehasEntry(name)、readEntry(name)正常可用readEntry返回的是同一 buffer 的一份新引用fz_keep_buffer而非拷贝——修改返回的 Buffer 会同时影响归档内条目countEntries()与listEntry(idx)不可用树归档只注册了has_entry、read_entry、open_entry三个回调没有实现count_entries/list_entry。从 source/fitz/archive.c 的实现看未注册这两个回调时fz_count_archive_entries返回 0、fz_list_archive_entry返回NULL即 JS 侧countEntries()恒为 0、listEntry(i)恒为null。如果需要遍历条目应由脚本自行维护名称列表。3. C 层实现剖析AA-tree 缓冲区引用JS 的new mupdf.TreeArchive()最终绑定到 C APIfz_new_tree_archive绑定代码见 source/tools/murun.c构造函数ffi_new_TreeArchive调用fz_new_tree_archive(ctx, NULL)创建空树TreeArchive.add对应的ffi_TreeArchive_add通过ffi_tonewbuffer把任意 JS 数据转成新 buffer 后调用fz_tree_archive_add_buffer。核心实现在 source/fitz/archive.ctypedef struct { fz_archive super; fz_tree *tree; } fz_tree_archive;数据容器是 AA-tree。fz_tree是一棵按字符串键查找的 AA 自平衡树include/mupdf/fitz/tree.hfz_tree_lookup按键查找、fz_tree_insert插入并返回重平衡后的新根、fz_drop_tree释放并对每个 value 调用 dropfunc。树中不拷贝键值只保存指针——键为规范化后的条目名值为fz_buffer指针。三个回调构成读路径fz_new_tree_archive中注册has_tree_entryfz_tree_lookup命中即返回 1read_tree_entry命中后执行fz_keep_buffer返回新引用——这正是 JS 侧readEntry“共享而非拷贝”语义的来源open_tree_entryfz_open_buffer把 buffer 包装成流供 C 层fz_open_archive_entry使用。写路径的类型保护。fz_tree_archive_add_buffer/fz_tree_archive_add_data都会先校验arch-super.has_entry has_tree_entry否则抛出FZ_ERROR_ARGUMENTcannot insert into a non-tree archive防止把 buffer 误插入 zip/tar 归档插入前对 buffer 执行fz_keep_buffer若fz_tree_insert抛异常则回滚引用并 rethrowsource/fitz/archive.c。生命周期。归档销毁时drop_tree_archive调用fz_drop_tree(ctx, tree, drop_tree_archive_entry)对每个条目 buffer 各释放一次引用与add时增加的引用配对无泄漏。此外C 层还提供两个 JS 未直接暴露的创建/写入入口include/mupdf/fitz/archive.hfz_new_tree_archive(ctx, tree)tree可为预先构建好的、值类型为fz_buffer的树或NULL表示空归档fz_tree_archive_add_data(ctx, arch, name, data, size)把一段裸内存拷贝进新 buffer 再入树调用方在返回后即可释放原数据——适合 C 代码快速装载add语义。4. 仓库中的实际应用MOBI 文档解析。source/html/mobi.c 在解析 MOBI 电子书时将解析出的各个内容块预先组织进fz_tree再调用fz_new_tree_archive(ctx, tree)一次性包装成归档使后续读取章节/资源文件走统一的归档接口——这是“C 侧预构建树 归档包装”的典型范例对应fz_new_tree_archive非空tree参数的用法。Java 绑定。platform/java/src/com/artifex/mupdf/fitz/TreeArchive.java 以public class TreeArchive extends Archive的形式暴露同一能力无参构造函数经 nativenewNativeTreeArchive()创建空归档public native void add(String name, Buffer buf)写入条目JNI 实现在 platform/java/jni/treearchive.c内部同样是fz_new_tree_archivefz_tree_archive_add_buffer的调用链。这说明 TreeArchive 的设计可写内存归档、读接口与 Archive 一致是跨语言统一复用的。5. 使用边界与注意事项适用环境JS API 仅在mutool脚本环境MuJS可用WASM/浏览器绑定未导出该类型C API 与 Java 绑定则各平台通用。无条目枚举能力countEntries()/listEntry()对树归档无意义恒为 0/null脚本侧需自行记录已添加的名称。引用共享语义readEntry返回的 Buffer 与归档条目共享同一份数据修改任一方都会互相影响add不接管传入 buffer 的所有权。条目名规范化查找路径经过fz_cleanname处理条目名应视为归档内唯一键重复add同名条目时新值会进入树AA-tree 插入会重新平衡语义上等同覆盖更新建议避免依赖同名条目的历史值。与 zip 的关系若最终需要产物落盘为 zip 文件应改用fz_zip_writer系列include/mupdf/fitz/archive.hTreeArchive定位是纯内存的中间态容器。参考路径官方类型文档docs/reference/javascript/types/TreeArchive.rst、docs/reference/javascript/types/Archive.rstC API 声明include/mupdf/fitz/archive.hfz_new_tree_archive、fz_tree_archive_add_buffer、fz_tree_archive_add_data实现source/fitz/archive.c、include/mupdf/fitz/tree.hAA-tree 定义JS 绑定source/tools/murun.c实际应用source/html/mobi.c、platform/java/src/com/artifex/mupdf/fitz/TreeArchive.java赞分享图形学图像处理【免费下载链接】mupdfmupdf mirror项目地址https://gitcode.com/gh_mirrors/mu/mupdf点击查看免费下载相关推荐MuPDF C API 实战指南Buffers Streams 缓冲区、流、过滤器与文件归档MuPDF C API 实战指南Buffers Streams 缓冲区、流、过滤器与文件归档 本指南系统讲解 MuPDF 的 C 语言 I/O 基础设施图形学图像处理MuPDF JavaScript API Text 对象实战指南构建、填充与遍历文本缓冲区MuPDF JavaScript API Text 对象实战指南构建、填充与遍历文本缓冲区 MuPDF 的 JavaScript 绑定中 Text 对象是图形学图像处理S3 Uploads完整指南从安装到迁移让WordPress附件存储上云S3 Uploads完整指南从安装到迁移让WordPress附件存储上云 S3 Uploads是一款轻量级WordPress插件能帮助用户将网站附件存储从后端存储创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询