使用Brotli压缩生成的PDF无法正常显示的原因咨询
Brotli压缩生成的PDF无法正常显示的原因咨询
我来帮你拆解下这个问题的可能原因,以及对应的验证方向:
核心问题分析
你遇到的情况大概率是PDF阅读器的兼容性问题,而非MuPDF的Brotli压缩实现错误,具体可以从这几个角度来看:
1. PDF规范对Brotli的支持版本限制
Brotli压缩是在PDF 2.0规范里才正式被纳入作为Flate(Deflate)压缩的替代选项的,而很多旧款PDF阅读器(比如Windows 8的默认PDF阅读器、旧版本的Edge/Firefox)仅支持到PDF 1.7及更早的规范,它们根本没有实现Brotli解压的逻辑,自然会显示空白页。
2. MuPDF生成PDF的版本默认设置
默认情况下,MuPDF生成的PDF版本可能是1.7,如果你在这个版本下使用Brotli压缩,本质上是不符合PDF 1.7规范的,阅读器看到1.7版本的PDF却遇到不认识的Brotli压缩流,就会直接忽略或无法解析。
你可以尝试修改写入选项,强制生成PDF 2.0版本:
const char* write_PDF_options = "compress=brotli,pdf-version=2.0";
生成后再用目标阅读器打开,看是否能正常显示。
3. 验证MuPDF生成的文件是否合规
你可以用MuPDF自带的mutool工具来验证自己生成的out_brotli.pdf:
- 用
mutool display d:\test_pdf\out_brotli.pdf打开,如果能正常显示,说明MuPDF的压缩实现是正确的,问题完全出在其他阅读器的兼容性上; - 用
mutool info d:\test_pdf\out_brotli.pdf查看PDF版本和流的压缩方式,确认Brotli标记和PDF版本是否匹配。
你的测试代码整理(方便参考)
#include <mupdf/fitz.h> #include <mupdf/pdf.h> int main(int argc, char **argv) { printf("-------------------- Test 2 (Brotli) -------------------\n"); // ------ settings ------ char input_pdf_fname[] = "d:\\test_pdf\\in.pdf"; char output_pdf_fname[] = "d:\\test_pdf\\out_brotli.pdf"; int cur_page_number = 0; // page number to read //const char* write_options = "compress=yes"; // deflate - works good const char* write_PDF_options = "compress=brotli"; // brotli - works bad // ----------------------- // total pages in PDF int total_pages_in_pdf; fz_context *ctx; fz_pixmap *pix; /* Create a context to hold the exception stack and various caches. */ ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); assert(ctx != nullptr); /* Register the default file types to handle. */ fz_register_document_handlers(ctx); /* Open the document. */ fz_document *doc = nullptr; fz_try(ctx) doc = fz_open_document(ctx, input_pdf_fname); fz_catch(ctx) { fz_report_error(ctx); exit(4); } pdf_document *pdf_doc = nullptr; fz_try(ctx) pdf_doc = pdf_specifics(ctx, doc); fz_catch(ctx) { fz_report_error(ctx); exit(4); } /* Count the number of pages. */ fz_try(ctx) total_pages_in_pdf = pdf_count_pages(ctx, pdf_doc); fz_catch(ctx) { assert(0); } printf("Total PDF pages = %d \n", total_pages_in_pdf); if (cur_page_number < 0 || cur_page_number >= total_pages_in_pdf) { fprintf(stderr, "page number out of range: cur=%d (total = %d)\n", cur_page_number, total_pages_in_pdf); pdf_drop_document(ctx, pdf_doc); fz_drop_context(ctx); return EXIT_FAILURE; } // -------- new PDF ----------- fz_document_writer *w; // Create a new pdf document fz_try(ctx) w = fz_new_pdf_writer(ctx, output_pdf_fname, write_PDF_options); fz_catch(ctx) { fz_report_error(ctx); exit(4); } printf("++++++ Copying page = %d ... ++++++\n", cur_page_number); printf(" From : %s \n", input_pdf_fname); printf(" To : %s \n", output_pdf_fname); printf(" PDF write options : %s \n", write_PDF_options); pdf_page *page = nullptr; fz_try(ctx) page = pdf_load_page(ctx, pdf_doc, cur_page_number); fz_catch(ctx) { page = nullptr; assert(0); } int ppi = 72; float scale_factor = (float)ppi / 72.0; int alpha = 0; // --------- create pixmap from pdf ---------------- fz_try(ctx) pix = pdf_new_pixmap_from_page_contents_with_usage( ctx, page, fz_identity, fz_device_rgb(ctx), alpha, NULL, FZ_CROP_BOX); fz_catch(ctx) { assert(0); } // Starts new page fz_device *out_device = nullptr; // output device to write a content of new page fz_irect rect_of_png = fz_pixmap_bbox(ctx, pix); fz_rect rect_of_pdf = fz_make_rect(rect_of_png.x0, rect_of_png.y0, rect_of_png.x1, rect_of_png.y1); fz_try(ctx) out_device = fz_begin_page(ctx, w, rect_of_pdf); // was: mediabox_crop fz_catch(ctx) { assert(0); } fz_rect page_bounds = pdf_bound_page(ctx, page, FZ_CROP_BOX); int keep_alpha = 0; fz_pixmap *rgb_pix = fz_convert_pixmap(ctx, pix, fz_device_rgb(ctx), NULL, NULL, fz_default_color_params, keep_alpha); fz_drop_pixmap(ctx, pix); fz_matrix img_ctm = fz_scale(page_bounds.x1 / fz_pixmap_width(ctx, rgb_pix), page_bounds.y1 / fz_pixmap_height(ctx, rgb_pix)); // write an image to the device fz_image *image = fz_new_image_from_pixmap(ctx, rgb_pix, NULL); float alpha_for_img = 1.0f; fz_fill_image(ctx, out_device, image, fz_scale(page_bounds.x1, page_bounds.y1), alpha_for_img, fz_default_color_params); // Ends new page fz_end_page(ctx, w); fz_drop_image(ctx, image); fz_drop_pixmap(ctx, pix); pix = nullptr; // Close the writer fz_close_document_writer(ctx, w); fz_drop_document_writer(ctx, w); pdf_drop_document(ctx, pdf_doc); fz_drop_context(ctx); return EXIT_SUCCESS; }
总结建议
- 先给写入选项加上
pdf-version=2.0,重新生成PDF后测试兼容性; - 用MuPDF自身的工具验证生成文件的合法性,确认是阅读器兼容性问题还是生成问题;
- 如果需要兼容旧阅读器,还是建议继续使用Deflate压缩,Brotli更适合只需要兼容现代PDF阅读器的场景。
内容来源于stack exchange




