You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用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;
}

总结建议

  1. 先给写入选项加上pdf-version=2.0,重新生成PDF后测试兼容性;
  2. 用MuPDF自身的工具验证生成文件的合法性,确认是阅读器兼容性问题还是生成问题;
  3. 如果需要兼容旧阅读器,还是建议继续使用Deflate压缩,Brotli更适合只需要兼容现代PDF阅读器的场景。

内容来源于stack exchange

火山引擎 最新活动