为何在MuPDF(C语言)中读取并释放PDF页面资源后无法创建像素图?
为何在MuPDF(C语言)中读取并释放PDF页面资源后无法创建像素图?
这个问题的核心是MuPDF的对象引用计数机制与页面渲染对资源字典的强依赖,我来一步步给你拆解原因:
首先你得理解MuPDF的对象生命周期规则:它靠引用计数追踪对象的存活状态,pdf_drop_obj会减少目标对象的引用计数,当计数降到0时,对象就会被自动销毁回收。
回到你的代码逻辑:
- 你通过
pdf_page_resources拿到的page_resources,是页面渲染(生成pixmap)时用来查找依赖资源(比如你的Img3这个XObject图片)的核心数据结构——页面里的所有图片、字体等资源,都要通过这个字典来定位。 - 你立刻调用
pdf_drop_obj(ctx, page_resources);(行A),这直接扣减了该资源字典的引用计数。如果这是最后一个有效的引用,MuPDF会直接把这个字典销毁掉。 - 后续调用
fz_new_pixmap_from_page_number生成像素图时,渲染进程内部需要解析页面的内容流:比如遇到Do操作(就是你在pdf-interpret.c里看到的pdf_process_Do函数),它会尝试从资源数据库(csi->rdb)中查找XObject资源。但这时候依赖的资源字典已经被你提前销毁了,自然找不到Img3,于是抛出了syntax error: cannot find XObject resource 'Img3'的错误。
为什么注释掉行A就正常?因为你没有提前释放page_resources,资源字典的引用计数维持在有效状态,渲染时它依然存在,能被正常用来查找XObject资源。
你的双文档workaround能生效,是因为两个pdf_doc是完全独立的实例,各自维护一套页面资源字典,所以从doc1读取并释放资源,不会影响doc2的渲染——但这确实是冗余的绕路操作。
正确的解决思路
你根本不需要打开第二个文档,只需要调整对象释放的时机或者引用计数的管理:
- 延迟释放资源字典:把
pdf_drop_obj(ctx, page_resources);移到生成pixmap并完成所有渲染相关操作之后:
src_page = pdf_load_page(ctx, pdf_doc, cur_page_number); pdf_obj* page_resources = pdf_page_resources(ctx, src_page); // 在这里执行你需要对page_resources做的操作 // 先不释放,先执行渲染 pix = fz_new_pixmap_from_page_number( ctx, doc, cur_page_number, fz_identity, fz_device_rgb(ctx), alpha); int pix_width = fz_pixmap_width(ctx, pix); int pix_height = fz_pixmap_height(ctx, pix); printf(" PIXMAP: W = %d H = %d \n", pix_width, pix_height); // 渲染完成后,再释放资源字典 pdf_drop_obj(ctx, page_resources);
- 如果必须提前释放自己的引用:可以用
pdf_keep_obj增加一次引用计数,这样你释放的只是自己获取的引用,页面内部的引用依然保留:
src_page = pdf_load_page(ctx, pdf_doc, cur_page_number); pdf_obj* page_resources = pdf_page_resources(ctx, src_page); // 增加引用计数,确保对象不会被提前销毁 pdf_keep_obj(ctx, page_resources); // 执行你需要对page_resources做的操作 pdf_drop_obj(ctx, page_resources); // 释放自己的引用,页面的引用还在 // 然后正常执行渲染逻辑 pix = fz_new_pixmap_from_page_number(...); // 最后记得释放页面 pdf_drop_page(ctx, src_page);
本质上你踩坑的点在于:页面的渲染操作完全依赖页面资源字典的存在,你不能在渲染完成前就把这个关键对象提前释放掉。MuPDF的渲染流程内部没有额外保留资源字典的引用,它依赖外部调用者正确维护对象的生命周期,直到渲染全部完成。
内容来源于stack exchange




