
图形学图像处理【免费下载链接】mupdfmupdf mirror项目地址https://gitcode.com/gh_mirrors/mu/mupdf点击查看免费下载本指南系统讲解 MuPDF 的 C 语言 I/O 基础设施fz_buffer动态字节缓冲区、可双向定位的fz_stream输入流、可链式组合的解码/解压/解密过滤器Filter、带缓冲与关闭语义的fz_output输出流以及fz_archive只读文件归档与fz_zip_writer写归档工具。读完本文你将掌握在 MuPDF 中读取、变换、编码、写出任意字节数据的完整调用链并理解其引用计数、所有权与关闭语义——这是编写 PDF/XPS/EPUB 文档处理工具的基础能力。本文对应的官方参考文档为 docs/reference/c/fitz/io.md全部函数原型可在 include/mupdf/fitz/buffer.h、include/mupdf/fitz/stream.h、include/mupdf/fitz/output.h、include/mupdf/fitz/filter.h、include/mupdf/fitz/archive.h 中逐一核对。Buffers通用字节容器在 MuPDF 中一段通用的数据块用fz_buffer结构表示。它本质上是对一块动态分配字节数组的封装既记录当前有效长度len也记录已分配的总容量cap还保存引用计数、位写入状态等内部字段typedef struct { unsigned char *data; size_t len; // current length size_t cap; // total capacity ... reserved internal fields ... } fz_buffer;实际定义include/mupdf/fitz/buffer.h还包含refs引用计数、unused_bits末字节未使用的位数配合位写入、shared是否共享外部数据等字段。官方头文件明确提示结构体内容属于实现细节可能随时变更用户应优先使用访问函数。Buffer 是引用计数对象两个基础生命周期函数贯穿始终fz_buffer *fz_keep_buffer(fz_context *ctx, fz_buffer *buf); void fz_drop_buffer(fz_context *ctx, fz_buffer *buf);fz_keep_buffer增加一次引用并返回同一指针绝不抛异常fz_drop_buffer减少一次引用引用计数归零时销毁缓冲区同样绝不抛异常见 buffer.h。创建缓冲区的多种途径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);: 创建一个引用给定数据指针的缓冲区。数据仅被引用缓冲区销毁时不会释放该数据在缓冲区存活期间数据指针不得改变或消失调用方必须保证其生命周期。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数据解码结果的缓冲区。此外头文件中还有几个常用变体值得留意buffer.hfz_new_buffer_from_data直接接管传入数据的所有权缓冲区释放时会fz_free该数据传入后不得再使用原指针fz_new_buffer_from_printf用 printf 格式生成内容fz_clone_buffer深拷贝一份独立缓冲区。逐段追加动态增长与格式化写入有时需要逐段拼装缓冲区内容同时让底层存储自动增长。MuPDF 提供一整套追加函数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(fz_context *ctx, fz_buffer *buf, int x); void fz_append_int16_le(fz_context *ctx, fz_buffer *buf, int x); void fz_append_int32_be(fz_context *ctx, fz_buffer *buf, int x); void fz_append_int32_le(fz_context *ctx, fz_buffer *buf, int x); void fz_append_printf(fz_context *ctx, fz_buffer *buffer, const char *fmt, ...); void fz_append_vprintf(fz_context *ctx, fz_buffer *buffer, const char *fmt, va_list args);追加整数时通过_be/_le后缀显式指定大端/小端字节序这在手工拼装二进制文件头如 ZIP、PNG时极为常用。fz_append_printf/fz_append_vprintf使用 MuPDF 自定义的 printf 格式化引擎见 output.h支持%c %d %s %u %x %X还支持零填充%02d、千位分隔符%d、%g最短无损输出以及%CUTF-8 编码整数、%Mfz_matrix*、%Rfz_rect*、%nPDF 名称等扩展格式。此外fz_append_buffer可把整个源缓冲区追加到目标缓冲区末尾fz_append_pdf_string会按 PDF 语法加引号并转义字符串。位流写入缓冲区还支持按位写入。缓冲区长度始终覆盖其中的所有位包括末字节中未使用的位这些未用位恒为零void fz_append_bits(fz_context *ctx, fz_buffer *buf, int value, int count);: 把value的低count位写入缓冲区。void fz_append_bits_pad(fz_context *ctx, fz_buffer *buf);: 写入足够的零位使缓冲区回到字节对齐状态。从源码看source/fitz/buffer.c位写入依赖buf-unused_bits记录末字节中未使用的位数先计算移位量把新位并入已有字节不足一字节时先扩展缓冲区容量fz_ensure_buffer再写入整字节最后把剩余位写到新的末字节并将unused_bits置为8 - bitsfz_append_bits_pad的实现就是把unused_bits清零。这类位操作在实现压缩/编码算法如把fz_read_bits读出的数据原样转存见 buffer.c时非常有用。作为 C 字符串使用与文件读写const char *fz_string_from_buffer(fz_context *ctx, fz_buffer *buf);调用后会在末字节之后补一个零终止符并返回指向首字节的指针。该指针只是借用只应在缓冲区再次被修改之前短暂使用——后续任何改变缓冲区大小的操作追加、fz_grow_buffer、fz_resize_buffer、fz_trim_buffer等都可能触发重新分配或释放使指针失效。官方同时提供显式的fz_terminate_buffer完成零终止buffer.h该零字节不参与缓冲区长度后续写入会覆盖它。缓冲区还可以直接与磁盘文件互换内容fz_buffer *fz_read_file(fz_context *ctx, const char *filename); void fz_save_buffer(fz_context *ctx, fz_buffer *buf, const char *filename);fz_read_file把整个文件读入缓冲区fz_save_buffer把缓冲区内容写入文件。配套的fz_try_read_file在文件不存在时返回 NULL 而非抛异常fz_read_text_file则按 UTF-8 文本读取为字符串stream.h。Input Streams可定位的输入流fz_stream是一个带缓冲、可在两个方向 seek 的读取器stream.h内部结构同样对用户不透明typedef struct { internal } fz_stream; fz_stream *fz_keep_stream(fz_context *ctx, fz_stream *stm); void fz_drop_stream(fz_context *ctx, fz_stream *stm);流同样采用引用计数管理fz_drop_stream在引用归零时释放流对象并通常释放其底层资源。底层结构stream.h包含refs、error、eof、progressive、pos、avail/bits位读取状态、一对读写指针rp/wp只有rp到wp之间的数据是有效的以及next/drop/seek三个函数指针。打开流fz_stream *fz_open_file(fz_context *ctx, const char *filename);: 打开文件内容作为流。非 Windows 平台下文件名与传给fopen(2)完全一致Windows 下应使用 UTF-8 编码路径见 stream.h。另有fz_try_open_file打不开时返回 NULL 而非抛异常、fz_open_file_autodelete关闭时自动删除文件、Windows 专用宽字符版本fz_open_file_w以及fz_stream_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_seek的whence与fseek一致SEEK_SET流起点、SEEK_CUR当前位置、SEEK_END流末尾。fz_read返回实际读到的字节数fz_skip丢弃指定字节数并返回实际跳过的字节数。两个一次性读取的便捷函数fz_buffer *fz_read_all(fz_context *ctx, fz_stream *stm, size_t initial);: 把剩余数据全部读入新缓冲区initial是建议的初始容量。其变体fz_read_best额外支持truncated标志与worst_case上限提示stream.h。char *fz_read_line(fz_context *ctx, fz_stream *stm, char *buf, size_t n);: 行为类似fgets()读到换行或 EOF并以\0替换终止符成功返回bufEOF 时且未读到任何字符返回 NULL。按字节读取的常用函数int fz_read_byte(fz_context *ctx, fz_stream *stm); int fz_peek_byte(fz_context *ctx, fz_stream *stm); int fz_is_eof(fz_context *ctx, fz_stream *stm);fz_read_byte返回下一个字节流结束返回-1EOFfz_peek_byte预读下一个字节但不移动读取位置fz_is_eof判断是否到达流尾。这些是 stream.h 中的内联函数字节读取优先消费rp/wp之间的缓冲数据缓冲耗尽时才调用底层的next回调重新填充并把读取错误按 EOF 处理记录stm-error并告警。fz_unread_byte可以把刚读过的单个字节退回去。按整数读取字节序控制可以一次读取一个二进制整数默认大端字节序同时提供小端_le版本uint16_t fz_read_uint16(fz_context *ctx, fz_stream *stm); uint32_t fz_read_uint24(fz_context *ctx, fz_stream *stm); uint32_t fz_read_uint32(fz_context *ctx, fz_stream *stm); uint64_t fz_read_uint64(fz_context *ctx, fz_stream *stm); uint16_t fz_read_uint16_le(fz_context *ctx, fz_stream *stm); uint32_t fz_read_uint24_le(fz_context *ctx, fz_stream *stm); uint32_t fz_read_uint32_le(fz_context *ctx, fz_stream *stm); uint64_t fz_read_uint64_le(fz_context *ctx, fz_stream *stm); int16_t fz_read_int16(fz_context *ctx, fz_stream *stm); int32_t fz_read_int32(fz_context *ctx, fz_stream *stm); int64_t fz_read_int64(fz_context *ctx, fz_stream *stm); int16_t fz_read_int16_le(fz_context *ctx, fz_stream *stm); int32_t fz_read_int32_le(fz_context *ctx, fz_stream *stm); int64_t fz_read_int64_le(fz_context *ctx, fz_stream *stm);注意fz_read_uint24读取 24 位3 字节无符号整数适合解析 PDF、BMP、TIFF 等格式的变长字段。这些函数在遇到 EOF 时会抛异常stream.h。另有fz_read_float/fz_read_float_le、fz_read_string读入指定长度的零终止字符串、fz_read_rune/fz_read_utf16_le/fz_read_utf16_be读取 UTF 编码字符等补充函数。位流读取按位读取同样内置支持unsigned int fz_read_bits(fz_context *ctx, fz_stream *stm, int n); unsigned int fz_read_rbits(fz_context *ctx, fz_stream *stm, int n); void fz_sync_bits(fz_context *ctx, fz_stream *stm); int fz_is_eof_bits(fz_context *ctx, fz_stream *stm);fz_read_bits按最高位在前读取n位n介于 1 到8*sizeof(int)之间EOF 时返回-1fz_read_rbits按最低位在前读取GIF、旧式 TIFF 等格式需要EOF 时返回(unsigned int)-1。读完位数据后调用fz_sync_bits重新同步到字节边界fz_is_eof_bits是位读取模式下的 EOF 判断见 stream.h。实现上流对象内的bits/avail字段保存尚未消费的位缓冲。Filters可链式组合的解码过滤器MuPDF 的各种解码、解压、解密过滤器都可以链式串联把前一个过滤器的输出接到下一个过滤器的输入形成一条解码管线例如先解密、再解压、再解码 ASCII 编码。以下是全部输入侧过滤器fz_stream *fz_open_null_filter(fz_context *ctx, fz_stream *chain, int len, int64_t offset); fz_stream *fz_open_arc4(fz_context *ctx, fz_stream *chain, unsigned char *key, unsigned keylen); fz_stream *fz_open_aesd(fz_context *ctx, fz_stream *chain, unsigned char *key, unsigned keylen); fz_stream *fz_open_a85d(fz_context *ctx, fz_stream *chain); fz_stream *fz_open_ahxd(fz_context *ctx, fz_stream *chain); fz_stream *fz_open_rld(fz_context *ctx, fz_stream *chain); fz_stream *fz_open_flated(fz_context *ctx, fz_stream *chain, int window_bits);各过滤器含义参数细节见 include/mupdf/fitz/filter.hfz_open_null_filter从链式流中读取指定长度len的数据从offset开始用于截取子流。fz_open_arc4用给定 key 对链式流做 RC4 解密。fz_open_aesdAES 解密配合 PDF 标准加密过滤器。fz_open_a85dASCII85 解码。fz_open_ahxdASCII Hex 解码。fz_open_rldRun Length 解码。fz_open_flatedLZ77 解压inflate。window_bits指定解压窗口大小典型值为15传负数-n表示使用n位窗口但期望无头部的裸数据。DCTJPEG、FAX、LZW、像素预测等更复杂的解码器参数较多fz_stream *fz_open_dctd(fz_context *ctx, fz_stream *chain, int color_transform, int invert_cmyk, int l2factor, fz_stream *jpegtables); fz_stream *fz_open_faxd(fz_context *ctx, fz_stream *chain, int k, int end_of_line, int encoded_byte_align, int columns, int rows, int end_of_block, int black_is_1); fz_stream *fz_open_lzwd(fz_context *ctx, fz_stream *chain, int early_change, int min_bits, int reverse_bits, int old_tiff); fz_stream *fz_open_predict(fz_context *ctx, fz_stream *chain, int predictor, int columns, int colors, int bpc);关键参数语义filter.hfz_open_dctdJPEG 解码color_transform对应 PDF 的 color_transform 选项——-1为默认行为0禁用 YUV→RGB / YCCK→CMYK 变换1启用invert_cmyk用于 Photoshop CMYK 图像的求反——PDF 内嵌取0非 PDF 取1l2factor是解码降采样比率的 log20表示全尺寸解码jpegtables是可选的外部 JPEG 表流不需要时传 NULL。fz_open_faxdFAX/CCITT 解码k默认0end_of_line默认0encoded_byte_align默认0columns默认1728rows为0表示未指定end_of_block默认1black_is_1默认0。fz_open_lzwdLZW 解码early_change默认1提前 1 位变更代码min_bits默认9reverse_bits默认0为兼容 GIF 与旧式 TIFF 可传1old_tiff默认0处理旧式 TIFF 中不同的 clear code 时传1。fz_open_predict像素预测predictor为1表示复制不解码、2表示 TIFF 预测、其他值表示内联 PNG 预测columns为图像像素宽度colors为颜色分量数bpc为每分量位数通常8。此外还有fz_open_jbig2dJBIG2 解压可选 globals 记录、fz_open_brotlidBrotli 解压、fz_open_libarchivedlibarchive 通用解压、TIFF 专用过滤器fz_open_sgilog16/24/32、fz_open_thunder、fz_open_concat/fz_concat_push_drop拼接多条流、fz_open_leecher旁路记录读取内容到缓冲区等filter.h、stream.h。链式用法示例解压一段先经 Flate 压缩再经 ASCIIHex 编码的 PDF 流只需fz_stream *stm fz_open_file(ctx, data.hex); stm fz_open_ahxd(ctx, stm); /* 先解 ASCIIHex */ stm fz_open_flated(ctx, stm, 15); /* 再解 Flate */ /* 现在从 stm 读出原始数据 */注意链的顺序数据先经过的编码最外层要最后套上解码器。这正是 PDF 解析器中过滤器链的通用组织方式——MuPDF 的 PDF 流解析代码在 source/pdf/pdf-stream.c 中根据 PDF 字典里的Filter数组逐一叠加对应解码器。Output Streams可链式组合的输出流输出流fz_output把数据写到某个汇sink通常是磁盘文件或缓冲区与输入流对称输出流也可以链式组合实现压缩、加密和编码后写出typedef struct { internal } fz_output;关闭语义close 与 drop 的区别由于写端可能存在缓冲MuPDF 为输出流定义了独立的关闭函数确保数据被正确 flush 并写完毕端标记。这与只负责释放内存的 drop 是两回事void fz_close_output(fz_context *ctx, fz_output *out); void fz_drop_output(fz_context *ctx, fz_output *out);使用规则非常明确output.h写操作成功后必须先fz_close_output再fz_drop_output写操作出错时直接fz_drop_output即可——既然写入没有完成正常 close 也就没有意义了千万不要忘了先 close 就直接 drop否则缓冲数据会丢失。fz_flush_output可以只 flush 未写出的数据而不关闭流fz_reset_output把已关闭的流恢复到打开时的状态并非所有输出类型都支持可能抛异常。创建输出流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);fz_new_output_with_path以 UTF-8 路径创建文件输出append非零表示追加而不是覆盖该流永远是二进制流fz_new_output_with_buffer创建向缓冲区追加的输出。此外还有fz_new_output_with_file_ptr包装现有FILE*、fz_stdout/fz_stderr/fz_stddbg标准输出/错误/调试流见 output.h。也可以完全自定义数据汇——提供状态指针和回调函数即可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));其中write在每次写入时被调用close负责 flush 并清理drop释放状态。buffer_size指定内部缓冲大小。fz_output结构output.h内部还支持seek/tell/reset/as_stream/truncate等可选回调以及bp/wp/ep缓冲指针和buffered/bits位写入状态。常用写入函数void fz_seek_output(fz_context *ctx, fz_output *out, int64_t off, int whence); int64_t fz_tell_output(fz_context *ctx, fz_output *out); void fz_write_data(fz_context *ctx, fz_output *out, const void *data, size_t size); void fz_write_string(fz_context *ctx, fz_output *out, const char *s); void fz_write_byte(fz_context *ctx, fz_output *out, unsigned char x); void fz_write_rune(fz_context *ctx, fz_output *out, int rune); void fz_write_int16_be(fz_context *ctx, fz_output *out, int x); void fz_write_int16_le(fz_context *ctx, fz_output *out, int x); void fz_write_int32_be(fz_context *ctx, fz_output *out, int x); void fz_write_int32_le(fz_context *ctx, fz_output *out, int x); void fz_write_printf(fz_context *ctx, fz_output *out, const char *fmt, ...); void fz_write_vprintf(fz_context *ctx, fz_output *out, const char *fmt, va_list ap); void fz_write_base64(fz_context *ctx, fz_output *out, const unsigned char *data, size_t size, int newline);要点fz_seek_output并非对所有输出类型都可用如管道/套接字输出不支持不可 seek 时抛异常fz_tell_output同理output.h。fz_write_string不写零终止符fz_write_rune写入 UTF-8 编码字符fz_write_base64输出 Base64 编码数据newline非零时周期性插入换行。头文件还提供fz_write_bits/fz_write_bits_sync按位写入最高位在前、fz_write_stream把整个输入流复制到输出、fz_write_buffer、fz_truncate_output在当前位置截断输出、fz_stream_from_output把输出当流读回读取期间不得再操作该输出对象等output.h。输出侧过滤器链输出流可以串联以实现加密、压缩、编码。重要这些过滤器不接管链式流的所有权它们只是向链式流写入fz_output *fz_new_arc4_output(fz_context *ctx, fz_output *chain, unsigned char *key, size_t keylen); fz_output *fz_new_ascii85_output(fz_context *ctx, fz_output *chain); fz_output *fz_new_asciihex_output(fz_context *ctx, fz_output *chain); fz_output *fz_new_deflate_output(fz_context *ctx, fz_output *chain, int effort, int no_header); fz_output *fz_new_rle_output(fz_context *ctx, fz_output *chain);典型用法官方注释给出的场景见 output.h先向底层输出写一个文件头创建压缩过滤器流如fz_new_deflate_output向过滤器写入要压缩的数据关闭过滤器flush 压缩数据继续向原输出写后续内容。例如ASCII Hex 编码 Deflate 压缩 RC4 加密写入缓冲区可以这样串联buf_out→fz_new_arc4_output→fz_new_deflate_output→fz_new_asciihex_output。必须按创建顺序的逆序关闭所有过滤器数据才能被正确 flushchain的所有权始终留在调用方手中需保证其存活到返回的fz_output被 drop 之后。File Archives文件归档的读写fz_archive是一个只读的文件集合抽象通常对应磁盘上的 Zip 文件或目录但也支持其他格式typedef struct { internal } fz_archive; void fz_drop_archive(fz_context *ctx, fz_archive *arch);fz_archive同样支持引用计数fz_keep_archive/fz_drop_archive底层通过一组虚函数指针drop_archive、count_entries、list_entry、has_entry、read_entry、open_entry分发到具体实现archive.h。MuPDF 内置目录、Zip、Tar、CFB 等实现还可通过fz_register_archive_handler注册自定义格式。打开归档int fz_is_directory(fz_context *ctx, const char *path); fz_archive *fz_open_directory(fz_context *ctx, const char *path); fz_archive *fz_open_archive(fz_context *ctx, const char *filename); fz_archive *fz_open_archive_with_stream(fz_context *ctx, fz_stream *file);fz_open_archive打开文件并根据内部签名识别归档类型Zip 或 Tarfz_open_archive_with_stream从可 seek 的流打开fz_try_open_archive_with_stream在无法识别格式时不抛异常archive.h。fz_open_directory把目录当作归档打开。注意对目录这种特殊归档无法获取条目数量或列出条目但可以检查是否存在某个条目archive.h。fz_is_directory判断路径是否为目录路径不存在或无权访问时返回0。归档类型可用fz_archive_format查询另有按格式强开的具体入口fz_open_zip_archive(_with_stream)、fz_open_tar_archive(_with_stream)、fz_open_libarchive_archive(_with_stream)、fz_open_cfb_archive(_with_stream)以及对应的格式探测函数fz_is_zip_archive、fz_is_tar_archive、fz_is_libarchive_archive、fz_is_cfb_archivearchive.h。基于内存的fz_open_zip_archive_with_memory可直接从静态数据打开 Zip。读取条目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_count_archive_entries返回条目数恒 0对无法计数的类型如目录会抛异常。fz_list_archive_entry返回第idx个条目的名字idx越界返回 NULL。fz_has_archive_entry按精确名称判断条目是否存在存在返回1否则0。fz_open_archive_entry以流方式打开条目找不到时抛异常fz_try_open_archive_entry返回 NULL。fz_read_archive_entry把条目全部读入缓冲区fz_try_read_archive_entry找不到时返回 NULL。这正是 MuPDF 读取 EPUB本质是 Zip 容器的路径EPUB 文档打开后会建立doc-archive正文、样式表等文件都通过fz_open_archive_entry(ctx, doc-archive, filename)以流方式读取见 source/pdf/pdf-stream.c 与 L576 的实际调用。创建 Zip 归档与只读的fz_archive相对MuPDF 还提供fz_zip_writer用于创建新的 Zip 文件typedef struct { internal } fz_zip_writer; fz_zip_writer *fz_new_zip_writer(fz_context *ctx, const char *filename); fz_zip_writer *fz_new_zip_writer_with_output(fz_context *ctx, fz_output *out); void fz_write_zip_entry(fz_context *ctx, fz_zip_writer *zip, const char *name, fz_buffer *buf, int compress); void fz_close_zip_writer(fz_context *ctx, fz_zip_writer *zip); void fz_drop_zip_writer(fz_context *ctx, fz_zip_writer *zip);fz_new_zip_writer创建写到指定文件的 writerfz_new_zip_writer_with_output写到给定输出流调用后输出的所有权立即转移给 writer即使函数抛异常调用方也不能再 drop 该输出archive.h。fz_write_zip_entry把缓冲区数据可选压缩以指定名称加入 Zip。fz_close_zip_writer关闭 Zip 并 flush 数据可能抛异常fz_drop_zip_writer释放 writer——若未先 close 就 drop会发出 dropping unclosed zip writer 警告。从实现看source/fitz/zip.cfz_write_zip_entry会用fz_crc32计算条目 CRC32通过fz_append_int32_le/fz_write_int32_le等小端写入函数拼装 ZIP 中央目录记录与本地文件头并设置 general purpose bit flag 的第 11 位标明 UTF-8 文件名fz_close_zip_writer在写出中央目录与结束记录后还会写入 5 字节的 MuPDF 文件注释。这里可以清楚看到前面讲的 buffer 追加函数、输出流写入函数在真实场景中的组合运用。完整实战示例缓冲区与流的基本生命周期结合 docs/examples/example.c 中标准的fz_context创建/释放模式一个最小的读写流程如下fz_context *ctx fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); if (!ctx) return EXIT_FAILURE; /* 1. 读文件进缓冲区再包一层输入流 */ fz_buffer *buf fz_read_file(ctx, input.bin); fz_stream *stm fz_open_buffer(ctx, buf); /* 2. 读取前 4 字节大端 uint32 */ uint32_t magic fz_read_uint32(ctx, stm); /* 3. 建一个输出流写入新文件转发剩余数据 */ fz_output *out fz_new_output_with_path(ctx, output.bin, 0); fz_write_stream(ctx, out, stm); /* 把流剩余内容复制到输出 */ fz_close_output(ctx, out); fz_drop_output(ctx, out); /* 4. 释放 */ fz_drop_stream(ctx, stm); fz_drop_buffer(ctx, buf); fz_drop_context(ctx);要点回顾输入流与缓冲区都是引用计数对象用完必须fz_drop_*输出流则先 close 再 drop。若写文件改为 Zip 场景只需把第 3 步替换为fz_new_zip_writerfz_write_zip_entryfz_close_zip_writerfz_drop_zip_writer。小结MuPDF 的 I/O 层由四个相互配合的抽象构成抽象头文件角色关键语义fz_bufferbuffer.h动态字节容器引用计数可追加、位写入、零终止fz_streamstream.h可 seek 的输入流引用计数字节/整数/位读取EOF 处理fz_outputoutput.h输出流无引用计数先 close 再 dropfz_archive/fz_zip_writerarchive.h归档读写只读归档 Zip 写入掌握这些接口后你就能在 MuPDF 中自由地读取任意来源文件、内存、缓冲区、归档条目、叠加解码过滤器、按字节序解析二进制结构以及把结果压缩/加密/编码后写回文件或缓冲区——这是深入理解 MuPDF 文档解析与输出管线的基础。相关实现可进一步阅读 source/fitz/buffer.c、source/fitz/zip.c 与 source/pdf/pdf-stream.c。赞分享图形学图像处理【免费下载链接】mupdfmupdf mirror项目地址https://gitcode.com/gh_mirrors/mu/mupdf点击查看免费下载相关推荐MuPDF 跨文档对象移植指南深入解析 PDFGraftMapJavaScript API 与 C 实现MuPDF 跨文档对象移植指南深入解析 PDFGraftMapJavaScript API 与 C 实现 MuPDF 的 PDFGraftMap 嫁接映桌面应用文档SumatraPDF 内置 MuPDF 的 I/O 层完全指南fz_buffer、fz_stream、过滤器与档案系统的原理与实战SumatraPDF 内置 MuPDF 的 I/O 层完全指南fz_buffer、fz_stream、过滤器与档案系统的原理与实战 导读 本文深入讲解当前仓库桌面应用文档Windows 驱动示例 SwapBuffers 详解文件系统微过滤器缓冲区交换与透明加密实现Windows 驱动示例 SwapBuffers 详解文件系统微过滤器缓冲区交换与透明加密实现 本篇技术指南围绕微软官方 Windows 驱动示例仓库中的 S示例工程上一篇financial-services 仓库 sector-overview 技能详解从市场格局到投资含义的行业全景报告生成指南下一篇Droppable 事件体系全解深入 Draggable 库的 droppable:start / dropped / returned / stop 四个核心事件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考