十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

深入解析 MuPDF C API 指南:从核心模块到 PDF 对象层的架构与实践(SumatraPDF 内嵌版)

深入解析 MuPDF C API 指南:从核心模块到 PDF 对象层的架构与实践(SumatraPDF 内嵌版) 桌面应用文档【免费下载链接】sumatrapdfSumatraPDF reader项目地址https://gitcode.com/gh_mirrors/su/sumatrapdf点击查看免费下载本指南基于 MuPDF 官方 C API 文档ext/mupdf/docs/reference/c/introduction.md系统梳理 MuPDF 对外提供的稳定公共接口。读者将掌握MuPDF 六大模块Core、I/O、Graphics、Device、Document、PDF的职责划分与协作关系、fz_context上下文与异常处理机制、多线程使用规则以及如何基于这些 API 写出可编译、可运行的渲染与文档处理代码。全文以本仓库内嵌的 MuPDF 源码与 SumatraPDF 集成代码src/EngineMupdf.cpp为佐证确保每个结论都有据可查。文档定位面向开发者的稳定 API 指南MuPDF 的 C API 指南introduction.md开宗明义这是一份面向开发者的库使用指南覆盖 MuPDF 库公开且稳定的部分。它有两个明确的受众目标新开发者想要使用 MuPDF 底层特性的入门向导有经验的开发者需要查阅公共接口的参考手册。文档范围特意限定在单线程应用的起步场景。MuPDF 内部还有更多函数与数据结构也支持多线程使用但这些超出本文档范围。文档强调其中记录的函数与结构是稳定 API很少变更若发生变更会在 api-changes 文档中记录迁移说明。注意本仓库SumatraPDF将 MuPDF 以子模块方式内嵌在 ext/mupdf 目录下完整的 C API 头文件位于 ext/mupdf/include/mupdf其中fitz子目录对应文档中的核心图形库。SumatraPDF 自身的 PDF 引擎src/EngineMupdf.cpp正是这套 API 的典型真实消费者。MuPDF 六大核心模块总览文档将公共 API 划分为六个功能模块从底层到顶层依次为模块职责对应头文件目录从源码结构看Core运行时上下文、异常处理、字符串操作、数学、哈希表、二叉树等基础工具ext/mupdf/include/mupdf/fitz 下context.h、geometry.h、hash.h、tree.h等I/O数据缓冲区、流读写、压缩、加密buffer.h、stream.h、compress.h、crypt.hGraphics颜色、字体、渐变着色shading、图像等图形资源对象color.h、font.h、shade.h、image.h、pixmap.h、path.hDevice文档内容的访问接口回调结构页面上的每段文本、线条、图像都会回调到设备device.h、display-list.h、structured-text.hDocument多格式文档读写串联上述模块提供渲染、格式转换、搜索document.h、link.h、outline.hPDFPDF 底层结构访问查询、修改、创建 PDF 对象与流可新建/修改文档、提取数据ext/mupdf/include/mupdf/pdf 下object.h、document.h、xref.h等Device理解 MuPDF 渲染与文本提取的关键抽象Device 接口是理解 MuPDF 架构的钥匙。文档明确指出A device is a callback structure, that gets called for each piece of text, line art, and image on a page.即设备是一个回调结构页面上的每个文本片段、每条矢量线条、每幅图像都会触发设备回调。MuPDF 有多个设备实现渲染设备把页面内容绘制到光栅图像pixmap上——这是最常见的使用方式文本收集设备把所有文本汇集为结构化结构供选择、复制、搜索页面文本使用。这种解释器 设备的设计让同一份页面内容可以绘制到不同目标上渲染成位图、提取文本、生成 SVG、写入显示列表等而不需要为每种输出重写文档解析逻辑。核心fz_context 上下文与资源管理文档指出 Core 模块包含运行时上下文runtime context、异常处理以及各类实用函数。要理解 MuPDF首先要理解贯穿几乎所有函数的第一个参数——fz_context。fz_context 是什么overview.md 详细说明了上下文的内容绝大多数 MuPDF 接口函数都接收一个 context 参数它保存了 MuPDF 在解析和渲染页面时使用的全局状态例如异常栈exception stack配合下文fz_try/fz_catch异常机制使用内存分配器memory allocator允许自定义分配器资源存储resource store缓存图像、字体等一组锁及加锁/解锁函数用于多线程场景。文档特别强调若未提供锁与配套函数context 及其代理只能用于单线程应用——这一限制是多线程部分的核心前提。创建与销毁 contextcontext.md 给出了最小可运行的创建示例#include mupdf/fitz.h #include stdio.h #include stdlib.h main() { fz_context *ctx fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); if (!ctx) { fprintf(stderr, Failed to create a new Fitz context!\n); return EXIT_FAILURE; } ... do stuff ... fz_drop_context(ctx); return EXIT_SUCCESS; }创建函数原型为fz_context *fz_new_context(const fz_alloc_context *alloc, const fz_locks_context *locks, size_t max_store);三个参数的含义参数说明取值建议alloc自定义内存分配器传NULL使用系统默认分配器locks线程安全所需的锁回调单线程传NULL多线程必须提供详见下文max_store资源存储缓存允许增长的最大字节数软限制如FZ_STORE_DEFAULT或FZ_STORE_UNLIMITED文档特别说明第三个参数是软限制缓存可能临时超过它但 MuPDF 会开始清理陈旧数据尽量压回限制以下内存紧张时设置较小值可防止缓存失控增长。销毁使用fz_drop_context(ctx)对应的引用计数递增函数是fz_clone_context(ctx)用于多线程克隆。异常处理fz_try / fz_always / fz_catch 体系MuPDF 采用基于setjmp/longjmp的异常处理体系由三个宏封装fz_try、fz_always、fz_catch。概念上类似 C 的 try/catch但不需要任何特殊编译器支持overview.md、error.md。基本结构fz_try(ctx) { // 尝试执行任务。绝不能从这里 return、goto 或 longjmp 出去。 // break 可用于安全退出仅try 块作用域。 } fz_always(ctx) { // 无论 try 块内是否抛出异常这里的代码都会执行。 // 同样绝不能 return、goto 或 longjmp 出去。 } fz_catch(ctx) { // 仅当 try 块包括其调用的任何函数抛出异常时 // 在 always 块之后执行。这里应处理异常记录/报告错误、 // 清理遗留状态等随后可退出该块 // 或用 fz_throw / fz_rethrow 将异常传给外层 fz_try 块。 }fz_always块可选可安全省略所有可能出错的函数调用都应放在fz_try块内否则出错时程序会直接调用exit()error.md 明确警告 You dont want thatfz_always的典型用途是无条件释放资源——无论 try 块成功还是出错。三条必须牢记的限制基于宏与setjmp的实现带来三个主要限制禁止从 try 块内 return/goto/longjmp这会破坏宏的内部簿记housekeeping后续会引发问题代码虽能检测到这类违规但为时已晚无法给出有用的错误定位。try/always/catch 不是一个原子的 C 语句。下面的写法是错的if (condition) fz_try(ctx) { ... } fz_catch(ctx) { ... } // 错误必须写成if (condition) { fz_try(ctx) { ... } fz_catch(ctx) { ... } }宏基于 setjmp/longjmp 实现因此 C 标准对这两个函数的所有限制都适用于fz_try/fz_catch。特别是在 fz_try 开始之后、到抛出异常之间被赋值的真正局部变量在异常抛出过程中可能变成未定义值。fz_var对抗变量丢失为缓解第三点MuPDF 提供fz_var()宏它告诉编译器确保该变量不会因抛出异常而被撤销。典型示例来自 error.mdchar *buf NULL; fz_var(buf); fz_try(ctx) { buf fz_malloc(ctx, 100); // Do stuff with buf that may throw an exception. } fz_always(ctx) { fz_free(ctx, buf); } fz_catch(ctx) { fz_rethrow(ctx); }若不用fz_var(buf)保护出错时局部变量buf可能被重置为进入 try 前的值NULL导致内存泄漏。抛出与重抛异常void fz_throw(fz_context *ctx, int error_code, const char *fmt, ...); void fz_rethrow(fz_context *ctx);fz_throw使用 printf 风格格式化字符串fz_rethrow用于在fz_catch块中完成清理后把异常继续上抛。错误码枚举来自 error.mdenum { FZ_ERROR_SYSTEM, // 致命内存耗尽或系统调用错误 FZ_ERROR_LIBRARY, // 第三方库的未分类错误 FZ_ERROR_ARGUMENT, // 传给函数的参数无效或越界 FZ_ERROR_LIMIT, // 资源或其他硬限制导致的失败 FZ_ERROR_UNSUPPORTED, // 尝试使用不支持的特性 FZ_ERROR_FORMAT, // 不可恢复的语法或格式错误 FZ_ERROR_SYNTAX, // 应诊断并忽略的语法错误 };此外还有fz_warn(ctx, warning: %s, msg)用于非致命告警以及fz_caught_message(ctx)用于在 catch 块中取得错误信息。完整范例build_houseoverview.md 提供了一个极具教学意义的完整模型代码展示了嵌套 try、备选方案回退、统一清理与重新抛出的全套模式house build_house(plans *p) { material m NULL; walls w NULL; roof r NULL; house h NULL; tiles t make_tiles(); fz_var(w); fz_var(r); fz_var(h); fz_try(ctx) { fz_try(ctx) { m make_bricks(); } fz_catch(ctx) { // 没有砖可用用稻草凑合 m make_straw(); } w make_walls(m, p); r make_roof(m, t); // 注意绝不能写 return combine(w,r); h combine(w, r); } fz_always(ctx) { drop_walls(w); drop_roof(r); drop_material(m); drop_tiles(t); } fz_catch(ctx) { fz_throw(ctx, build_house failed); } return h; }要点make_tiles()若抛异常会直接被更外层处理器接管若成功t在fz_try开始前已赋值因此无需fz_var(t)先尝试做砖失败则回退到稻草再失败则落入fz_catch整个流程干净失败假设combine对传入的 walls 和 roof 各取一份新引用因此w、r在所有情况下都要清理遵循标准 C 约定销毁NULL是安全的。内存管理分配器、池与引用计数memory.md 说明Fitz 中所有内存都通过分配器分配可按需替换为自定义分配器。分配与释放void *fz_malloc(fz_context *ctx, size_t size); void *fz_realloc(fz_context *ctx, void *old, size_t size); void *fz_calloc(fz_context *ctx, size_t count, size_t size); void fz_free(fz_context *ctx, void *ptr);与标准 C 函数的差别它们不会返回 NULL——要么成功要么抛异常如FZ_ERROR_MEMORY。另有带类型转换的宏T *fz_malloc_struct(fz_context *ctx, T); // 分配并清零 T *fz_malloc_array(fz_context *ctx, size_t count, T); // 分配不初始化 T *fz_realloc_array(fz_context *ctx, T *old, size_t count, T);极少数需要失败返回NULL的场景可用fz_malloc_no_throw等变体。池分配器用于批量分配同生共死的小对象池释放时其上的所有对象一并释放fz_pool *fz_new_pool(fz_context *ctx); void *fz_pool_alloc(fz_context *ctx, fz_pool *pool, size_t size); char *fz_pool_strdup(fz_context *ctx, fz_pool *pool, const char *s); void fz_drop_pool(fz_context *ctx, fz_pool *pool);引用计数keep / drop 约定MuPDF 中大多数对象用引用计数管理生命周期动词约定为keep递增与drop递减为统一接口非引用计数对象也使用 drop 命名——这样将来给对象加上引用计数时调用方代码无需改动。例如 pixmap 的 api-overview.md 所示fz_pixmap *fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix); // 递增引用计数 void fz_drop_pixmap(fz_context *ctx, fz_pixmap *pix); // 减到 0 时释放I/O 模块缓冲区、流、过滤器与归档io.md 系统介绍了 I/O 模块包括数据缓冲区、输入/输出流、可链式组合的解码过滤器以及文件归档archive抽象。缓冲区 fz_bufferfz_buffer表示通用的数据块公开字段为typedef struct { unsigned char *data; size_t len; // 当前长度 size_t cap; // 总容量 ... 保留内部字段 ... } fz_buffer;创建方式多种多样fz_buffer *fz_new_buffer(fz_context *ctx, size_t capacity); // 空缓冲区给定初始容量 fz_buffer *fz_new_buffer_from_shared_data(fz_context *ctx, const unsigned char *data, size_t size); // 只引用不持有data 在缓冲区存活期间不得变动/消失 fz_buffer *fz_new_buffer_from_copied_data(fz_context *ctx, const unsigned char *data, size_t size); // 拷贝数据 fz_buffer *fz_new_buffer_from_base64(fz_context *ctx, const char *data, size_t size); // 解码 BASE64动态追加数据自动扩容void fz_append_data(fz_context *ctx, fz_buffer *buf, const void *data, size_t len); void fz_append_string(fz_context *ctx, fz_buffer *buf, const char *string); void fz_append_byte(fz_context *ctx, fz_buffer *buf, int byte); void fz_append_rune(fz_context *ctx, fz_buffer *buf, int rune); void fz_append_int16_be/le(fz_context *ctx, fz_buffer *buf, int x); // 大小端整数 void fz_append_int32_be/le(fz_context *ctx, fz_buffer *buf, int x); void fz_append_printf(fz_context *ctx, fz_buffer *buffer, const char *fmt, ...);还可写入位流fz_append_bits写入 value 的低 count 位、fz_append_bits_pad补零到字节对齐缓冲区长度始终覆盖所有位最后一字节未用位恒为 0。fz_string_from_buffer可取得以零结尾的 C 字符串指针借用指针仅在缓冲区再次改动前短暂使用。文件操作fz_read_file读文件入缓冲区、fz_save_buffer存缓冲区到文件。输入流 fz_stream流是数据的读取源部分流类型可解压/解密且可以链式组合成管道fz_stream *fz_open_file(fz_context *ctx, const char *filename); // 读文件 fz_stream *fz_open_memory(fz_context *ctx, const unsigned char *data, size_t len); // 读内存 fz_stream *fz_open_buffer(fz_context *ctx, fz_buffer *buf); // 读缓冲区基础操作int64_t fz_tell(fz_context *ctx, fz_stream *stm); void fz_seek(fz_context *ctx, fz_stream *stm, int64_t offset, int whence); size_t fz_read(fz_context *ctx, fz_stream *stm, unsigned char *data, size_t len); size_t fz_skip(fz_context *ctx, fz_stream *stm, size_t len); fz_buffer *fz_read_all(fz_context *ctx, fz_stream *stm, size_t initial); char *fz_read_line(fz_context *ctx, fz_stream *stm, char *buf, size_t n); // 类似 fgets() int fz_read_byte / fz_peek_byte / fz_is_eof(...);定长整数读取默认大端另有_le小端变体uint16_t fz_read_uint16; uint32_t fz_read_uint24; uint32_t fz_read_uint32; uint64_t fz_read_uint64; int16_t fz_read_int16; int32_t fz_read_int32; int64_t fz_read_int64;位流读取fz_read_bits、fz_read_rbits、fz_sync_bits、fz_is_eof_bits。过滤器解码/解压/解密管道解码过滤器可链在输入流上例如fz_open_flatedFlate/zlib 解压、fz_open_a85dASCII85 解码、fz_open_ahxdASCIIHex 解码、fz_open_rldRunLength 解码、fz_open_dctdJPEG 解码可传色彩变换、CMYK 反转、缩放因子与 JPEG 表流、fz_open_faxdCCITT Fax 解码含 k、行长、字节对齐、黑白反转等参数、fz_open_lzwdLZW 解码、fz_open_predictPredictor 解码器用于 PDF 中的 PNG/TIFF 预测、fz_open_arc4/fz_open_aesdARCFOUR / AES 解密、fz_open_null_filter截断流。输出流 fz_output输出流写入数据到汇点通常是文件或缓冲区同样可链式组合以压缩、加密、编码。关键区别写操作成功后必须先fz_close_output再fz_drop_output——close 负责刷新缓冲并写出结束标记drop 仅释放内存写数据出错时可直接 drop无法正常收尾。fz_output *fz_new_output_with_path(fz_context *, const char *filename, int append); fz_output *fz_new_output_with_buffer(fz_context *ctx, fz_buffer *buf); // 自定义汇点提供 state 指针与 write/close/drop 回调 fz_output *fz_new_output(fz_context *ctx, int buffer_size, void *state, void (*write)(fz_context *ctx, void *state, const void *data, size_t n), void (*close)(fz_context *ctx, void *state), void (*drop)(fz_context *ctx, void *state));写函数族fz_write_data/string/byte/rune、fz_write_int16_be/le、fz_write_int32_be/le、fz_write_printf/vprintf、fz_write_base64可换行。链式输出过滤器fz_new_arc4_output、fz_new_ascii85_output、fz_new_asciihex_output、fz_new_deflate_output带压缩 effort 与头部选项、fz_new_rle_output。注意这些过滤器不接管被链流的所有权仅向其写入——可先写头部、创建压缩过滤器、写入数据、关闭过滤器、再继续写原流。文件归档 fz_archive归档是只读文件集合抽象典型是 ZIP 或磁盘目录也支持其他格式fz_archive *fz_open_directory(fz_context *ctx, const char *path); fz_archive *fz_open_archive(fz_context *ctx, const char *filename); // 自动探测 ZIP/TAR fz_archive *fz_open_archive_with_stream(fz_context *ctx, fz_stream *file); int fz_count_archive_entries(fz_context *ctx, fz_archive *arch); const char *fz_list_archive_entry(fz_context *ctx, fz_archive *arch, int idx); int fz_has_archive_entry(fz_context *ctx, fz_archive *arch, const char *name); fz_stream *fz_open_archive_entry(fz_context *ctx, fz_archive *arch, const char *name); fz_buffer *fz_read_archive_entry(fz_context *ctx, fz_archive *arch, const char *name);反向操作fz_zip_writer可创建新的 ZIP 归档fz_new_zip_writer、fz_write_zip_entry带压缩开关、fz_close_zip_writer、fz_drop_zip_writer。EPUB本质是 ZIP等复合格式文档正是依赖这套归档接口解析的。Graphics 与渲染资源Graphics 模块提供图形资源对象颜色、字体、渐变、图像以及承载渲染结果的 pixmap。结合 api-overview.md 可得到这些资源的要点颜色空间fz_colorspace常用fz_device_rgb(ctx)等设备颜色空间fz_convert_pixmap可做颜色空间转换含打样空间、颜色参数、是否保留 alpha。pixmap光栅图像对象公开访问器有fz_pixmap_width/height/x/y/stride/components/samples/colorspace等创建方式包括fz_new_pixmap指定颜色空间、宽高、分色与 alpha、fz_new_pixmap_with_bbox、fz_new_pixmap_with_data包装既有缓冲区不负责释放。常用操作fz_clear_pixmap、fz_scale_pixmap、fz_invert_pixmap、fz_gamma_pixmap、fz_clone_pixmap。bitmap1 位/分量半色调位图fz_new_bitmap_from_pixmap由 pixmap 生成用于打印输出样本 MSB 优先、兼容 PBM 格式。path矢量路径fz_moveto/lineto/curveto/closepath/rectto构建fz_bound_path求包围盒描边参数fz_stroke_state含线帽FZ_LINECAP_BUTT/ROUND/SQUARE/TRIANGLE、线连接FZ_LINEJOIN_MITER/ROUND/BEVEL/MITER_XPS、线宽、斜接限制与虚线序列。字体fz_font配合 glyph 缓存用于文本渲染。文档模块打开、鉴权、分页、渲染与搜索Document 模块是使用频率最高的高层入口。结合 api-overview.md打开文档与鉴权fz_document *fz_open_document(fz_context *ctx, const char *filename); // 自动探测格式 fz_document *fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream); fz_document *fz_open_accelerated_document(fz_context *ctx, const char *filename, const char *accel); int fz_needs_password(fz_context *ctx, fz_document *doc); int fz_authenticate_password(fz_context *ctx, fz_document *doc, const char *password);页数、页面加载与页面边界int fz_count_pages(fz_context *ctx, fz_document *doc); // 总页数 int fz_count_chapters(fz_context *ctx, fz_document *doc); // 章数EPUB 等多章格式 fz_page *fz_load_page(fz_context *ctx, fz_document *doc, int number); // 按扁平页号从 0 开始 fz_page *fz_load_chapter_page(fz_context *ctx, fz_document *doc, int chapter, int page); fz_rect fz_bound_page(fz_context *ctx, fz_page *page); // 页包围盒pty 轴向下注意文档明确API 中页号一律从 0 开始zero-based。渲染void fz_run_page(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie);fz_run_page把页面内容 注释 表单控件渲染到设备另有fz_run_page_contents仅内容流、fz_run_page_annots仅注释、fz_run_page_widgets仅表单控件。fz_cookie用于渲染过程中的进度/中止控制。高阶一步到位接口在 ext/mupdf/include/mupdf/fitz/util.hfz_pixmap *fz_new_pixmap_from_page(fz_context *ctx, fz_page *page, fz_matrix ctm, fz_colorspace *cs, int alpha); fz_pixmap *fz_new_pixmap_from_page_number(fz_context *ctx, fz_document *doc, int number, fz_matrix ctm, fz_colorspace *cs, int alpha);文本搜索与显示列表搜索接口返回命中四边形fz_quad数组int fz_search_page(fz_context *ctx, fz_page *page, const char *needle, int *hit_mark, fz_quad *hit_bbox, int hit_max);显示列表把页面渲染录制为可复用命令序列多线程部分的关键角色fz_display_list *fz_new_display_list_from_page(fz_context *ctx, fz_page *page); fz_pixmap *fz_new_pixmap_from_display_list(fz_context *ctx, fz_display_list *list, fz_matrix ctm, fz_colorspace *cs, int alpha);元数据与书签int fz_lookup_metadata(fz_context *ctx, fz_document *doc, const char *key, char *buf, size_t size);标准键包括format、encryption、info:Title、info:Author等返回字符串长度未找到返回 -1。书签用于跨会话恢复阅读位置fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, fz_location loc); fz_location fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark);可重排文档EPUB、FB2 等使用fz_layout_document(ctx, doc, w, h, em)设定页面布局配套预置常量如FZ_LAYOUT_KINDLE_W/H/EM、FZ_LAYOUT_A5_W/H/EM。从 Hello World 到可运行程序快速上手示例api-overview.md 给出了一个把 PDF 第 0 页以 150 dpi 渲染为 PNG 的完整示例集成了本文档介绍的 context、异常处理、文档/页面/pixmap 生命周期#include mupdf/fitz.h int main(void) { fz_context *ctx fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); fz_register_document_handlers(ctx); fz_document *doc NULL; fz_page *page NULL; fz_pixmap *pix NULL; fz_try(ctx) { doc fz_open_document(ctx, input.pdf); page fz_load_page(ctx, doc, 0); /* 150 dpi 150/72 scale */ fz_matrix ctm fz_scale(150.0f / 72, 150.0f / 72); pix fz_new_pixmap_from_page(ctx, page, ctm, fz_device_rgb(ctx), 0); fz_save_pixmap_as_png(ctx, pix, output.png); } fz_always(ctx) { fz_drop_pixmap(ctx, pix); fz_drop_page(ctx, page); fz_drop_document(ctx, doc); } fz_catch(ctx) { fprintf(stderr, error: %s\n, fz_caught_message(ctx)); } fz_drop_context(ctx); return 0; }注意其结构完全遵循前文规则fz_new_context(NULL, NULL, FZ_STORE_DEFAULT)单线程创建、所有可能抛异常的操作收拢在fz_try内、fz_always中统一 drop且 drop 顺序与创建相反、fz_catch打印fz_caught_message。本仓库 ext/mupdf/docs/examples 下还有更完整的example.c、multi-threaded.c、searchtest.c、storytest.c等配套示例可作为进一步研读的起点。多线程使用五条铁律与 context 克隆虽然本指南定位单线程但 overview.md 对多线程做了完整阐述。首先明确如果文档在一个线程中打开并充当服务器为其他线程提供页面渲染服务MuPDF 始终只被单线程调用则完全无需加锁这是最简单高效的模式。真正需要并发调用时须遵守以下五条规则不同线程不得同时调用使用同一 context——最简单做法是每线程一个 context通过克隆获得不同线程不得同时调用使用同一 document——同一时刻仅一个线程可访问文档但从该文档创建的显示列表可被多线程同时操作不同线程不得同时调用使用同一 device——并发调用 device 会使其状态错乱甚至崩溃除非纯单线程否则创建 context 时必须提供fz_locks_context——即使使用完全独立的 MuPDF 实例MuPDF 仍需借用户提供的锁保护跨线程的共享结构/资源/库所有 context 必须共享同一个fz_locks_context或其底层锁——强烈建议只调用一次fz_new_context之后用fz_clone_context派生新 context虽然目前仍支持多次fz_new_context创建完全独立的 context但必须共享同一锁集且该能力未来可能移除。锁的实现细节调用方应提供FZ_LOCK_MAX个互斥量MuPDF 通过回调以用户指针 锁编号 i0 ≤ i FZ_LOCK_MAX方式加锁/解锁互斥量递归或非递归均可MuPDF 只做非递归调用。克隆 context 的原理每个 context 含一个异常栈嵌套的fz_try/fz_catch会操作它显然同一异常栈不能被多线程同时使用。但若每线程fz_new_context一个新 context则会得到互相独立的 store/glyph cache通常不是我们想要的。fz_clone_context因此而生新 context 与给定 context 共享除异常栈以外的一切store、字形缓存等且每个克隆体仍可用fz_free_context单独释放。通用方案程序启动时创建一个基础 context随后反复克隆出供各线程使用的 context。多线程并发模式下典型架构二选一由单一指定线程打开文档并充当服务器为其他线程生成显示列表长期看更高效或自行加互斥锁保护对文档的所有 MuPDF 调用。可参考 ext/mupdf/docs/examples/multi-threaded.c——它演示了一个主线程加每页一个渲染线程的模式。PDF 模块对象与流的底层访问Document 之上的 PDF 模块提供底层 PDF 结构访问能力introduction.md。其核心价值在于当高层 API 不能满足需求时可以直接在 PDF 对象object与流stream层面查询检查文档特性、提取数据修改修改既有文档的对象与流创建创建新的 PDF 对象、流乃至全新文档。对应头文件集中在 ext/mupdf/include/mupdf/pdfobject.hPDF 对象模型间接引用、字典、数组、名称树等、document.hpdf_document与pdf_page的创建/保存、xref.h交叉引用表与流读取、annot.h注释、form.h表单字段、clean.h文档清理、javascript.h、recolor.h、zugferd.h等。SumatraPDF 的引擎封装src/EngineMupdf.cpp即同时调用了 fitz 层渲染 API 与 PDF 层对象 API是理解两层协作的现成实例。实用 API 速查与约定包含方式推荐通过伞形头文件包含整个公共 APIext/mupdf/include/mupdf/fitz.h#include mupdf/fitz.h该头文件按顺序聚合 Coreversion.h、config.h、system.h、context.h、output.h、log.h、工具crypt.h、geometry.h、hash.h、xml.h、json.h等、I/Obuffer.h、stream.h、filter.h、archive.h、资源store.h、color.h、pixmap.h、image.h、font.h、path.h、text.h等、渲染device.h、display-list.h、structured-text.h、glyph-cache.h与文档link.h、outline.h、document.h各组头文件。三条全局约定来自 api-overview.md所有字符串参数默认UTF-8 编码除非另有说明全程页号从 0 开始fz_context非线程安全——每线程用fz_clone_context建独立 context并通过锁共享资源存储内存管理遵循keep/drop模式fz_keep_*递增引用计数fz_drop_*递减并在归零时释放。总结MuPDF 的 C API 设计层次清晰Core提供上下文与异常地基I/O提供缓冲/流/过滤器的数据管道Graphics提供渲染所需的资源对象Device以回调抽象解耦内容解析与内容输出Document在高层串起渲染、转换与搜索PDF则在底层暴露对象与流级别的完全控制。对开发者而言抓住三条主线即可快速上手一切以fz_context为入口、一切可能失败的调用放进fz_try/fz_catch、一切资源遵循 keep/drop 与fz_always统一清理。本仓库中SumatraPDF 的 src/EngineMupdf.cpp 与 ext/mupdf/docs/examples 下的示例程序是这套 API 从文档走向生产代码的最佳范本。赞分享桌面应用文档【免费下载链接】sumatrapdfSumatraPDF reader项目地址https://gitcode.com/gh_mirrors/su/sumatrapdf点击查看免费下载相关推荐Parabolic终极指南免费高效的跨平台视频音频下载器Parabolic终极指南免费高效的跨平台视频音频下载器 Parabolic是一款基于yt dlp的强大开源视频音频下载工具支持数百个网站和多种格式的媒体下桌面应用文档Mongood性能优化技巧强制索引查询功能详解Mongood性能优化技巧强制索引查询功能详解 作为一名MongoDB开发者你是否曾为查询性能问题而烦恼Mongood作为一款现代化的MongoDB GU桌面应用文档SumatraPDF 内核解析MuPDF JavaScript 绑定中的 Link 链接对象getBounds / getURI / isExternal 全指南SumatraPDF 内核解析MuPDF JavaScript 绑定中的 Link 链接对象getBounds / getURI / isExternal桌面应用文档上一篇CocoaLumberjack性能调优指南从源码级别优化日志效率下一篇如何永久保存微信聊天记录WeChatMsg完整使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表