You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

muPDF/C中渲染像素图后输出PDF显示空页的问题求助

muPDF/C中渲染像素图后输出PDF显示空页的问题求助

我最近在做muPDF相关开发时碰到了个头疼的问题:我把输入PDF的某一页渲染成像素图(pixmap)后,尝试将这个像素图写入新的输出PDF文件,但打开输出PDF时显示的是空页;不过只要取消注释DrawPathOnPage函数的调用,输出PDF就能正常显示红色矩形,这说明输出设备本身是工作正常的。而且我把pixmap保存成PNG时能正常显示内容,就是没法正确输出到PDF里,有没有大佬能帮我看看哪里出问题了?

以下是我的完整代码:

#include <mupdf/fitz.h>
#include <mupdf/fitz/context.h>
extern "C" {
#include <mupdf/pdf/document.h> // pdf
}
#include <mupdf/fitz/pixmap.h>
#include <mupdf/fitz/geometry.h>
#include <mupdf/fitz/version.h>
#include <mupdf/fitz/util.h>
#include <mupdf/fitz/color.h>
#include <mupdf/fitz/system.h>
extern "C" {
#include <mupdf/pdf/page.h> // page_pdf
}
extern "C" {
#include <mupdf/pdf/xref.h> // pdf_trailer
}
#include <stdio.h>
#include <stdlib.h>
#include "MyUtils_LIB.h"

void print_rect(const char* prefix, fz_rect r) {
    printf(" %s %d x %d --- %d x %d \n", prefix, (int)r.x0, (int)r.y0, (int)r.x1, (int)r.y1);
}

void DrawPathOnPage(fz_context* ctx, fz_device *dev //, pdf_document* dst_doc, int dst_page_num ) {
    // Draws a path in page #dst_page_num of the opened dst_doc.
    // === create path ===
    fz_path* path = fz_new_path(ctx);
    // === fill rectangle ===
    float x0 = 0;
    float y0 = 0;
    float x1 = 100;
    float y1 = 100;
    fz_rectto(ctx, path, x0, y0, x1, y1);
    fz_closepath(ctx, path);
    // load a page to modify
    //pdf_load_page(ctx, dst_doc, dst_page_num); // (Q) : how to link a device with a given page ???
    //fz_device *dev = create_dev();
    // write path
    float alpha = 0.9f;
    int even_odd = 0;
    fz_matrix ctm = fz_scale(1, 1);
    //fz_color_params fz_default_color_params;
    float color[3] ={1.0f, 0.0f, 0.0f};
    fz_fill_path(ctx, dev, path, even_odd, ctm, fz_device_rgb(ctx), color, alpha, fz_default_color_params);
    fz_closepath(ctx, path);
} // void DrawPathOnPage

int main(int argc, char **argv) {
    printf("-------------------- Test 2 (small) -------------------\n");
    // file names
    const char output_png_fname_mask[] = "d:\\test_pdf\\out_%d.png";
    char input_pdf_fname[] = "d:\\test_pdf\\in.pdf";
    char output_png_fname[200];
    char output_pdf_fname[] = "d:\\test_pdf\\out.pdf";
    int cur_page_number = 0; // page number to copy from
    // used to transform PDF page
    float zoom = 100;
    float rotate = 0;
    // total pages in PDF
    int total_pages_in_pdf;

    fz_context *ctx;
    fz_pixmap *pix;
    fz_matrix ctm;
    int x, y;

    /* 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) {
        assert(0);
    }

    pdf_document *pdf_doc = nullptr;
    fz_try(ctx)
        pdf_doc = pdf_specifics(ctx, doc);
    fz_catch(ctx) {
        assert(0);
    }

    /* 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;
    const char *options_pdf = NULL;
    // Create a new pdf document
    fz_try(ctx)
        w = fz_new_pdf_writer(ctx, output_pdf_fname, options_pdf);
    fz_catch(ctx) {
        assert(0);
    }

    printf("++++++ Copying page = %d ... ++++++\n", cur_page_number);
    printf(" From : %s\n", input_pdf_fname);
    printf(" To : %s\n", output_pdf_fname);

    pdf_page *page = nullptr;
    fz_try(ctx)
        page = pdf_load_page(ctx, pdf_doc, cur_page_number);
    fz_catch(ctx) {
        page = nullptr;
        assert(0);
    }

    // Output page boxes sizes
    fz_rect mediabox_media;
    fz_matrix ctm_media;
    pdf_page_transform_box(ctx, page, &mediabox_media, &ctm_media, FZ_MEDIA_BOX);
    print_rect(" :MEDIA BOX: ", mediabox_media);
    // 原代码错误复用了mediabox,这里修正为获取Crop Box
    fz_rect crop_box = pdf_page_bound_box(ctx, page, FZ_CROP_BOX);
    print_rect(" :CROP BOX: ", crop_box);

    int ppi = 72;
    float scale_factor = (float)ppi / 72.0;
    int alpha = 0;
    const char *usage = NULL;
    fz_box_type box_type = FZ_CROP_BOX;

    // --------- 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, box_type);
    fz_catch(ctx) {
        assert(0);
    }

    // ------------------- 原代码缺失的关键绘制步骤 -------------------
    // 1. 为输出PDF创建新页面,获取对应的绘制设备
    fz_device *out_dev = fz_new_pdf_page(ctx, w, crop_box.x1 - crop_box.x0, crop_box.y1 - crop_box.y0, 0);
    if (!out_dev) {
        fprintf(stderr, "Failed to create output page device\n");
        goto cleanup;
    }

    // 2. 设置变换矩阵:将pixmap对齐到页面左上角(因为原页面的Crop Box可能不是从(0,0)开始)
    fz_matrix draw_ctm = fz_translate(-crop_box.x0, -crop_box.y0);
    // 如果pixmap使用了非72PPI的分辨率,需要添加缩放,比如:
    // draw_ctm = fz_concat(draw_ctm, fz_scale(scale_factor, scale_factor));

    // 3. 将pixmap绘制到输出设备上
    fz_draw_pixmap(ctx, out_dev, draw_ctm, pix, NULL, NULL, 1.0f);

    // 4. 可选:取消注释可验证设备工作状态
    // DrawPathOnPage(ctx, out_dev);

    // 5. 关闭设备并提交页面到PDF writer
    fz_close_device(ctx, out_dev);
    fz_drop_device(ctx, out_dev);

    // ------------------- 绘制步骤结束 -------------------

    // 保存pixmap为PNG(可选)
    sprintf(output_png_fname, output_png_fname_mask, cur_page_number);
    fz_save_pixmap(ctx, pix, output_png_fname, NULL);
    printf("Saved PNG to %s\n", output_png_fname);

cleanup:
    // 释放所有muPDF资源,避免内存泄漏
    if (pix) fz_drop_pixmap(ctx, pix);
    if (page) fz_drop_page(ctx, (fz_page*)page);
    if (w) fz_close_document_writer(ctx, w);
    if (pdf_doc) pdf_drop_document(ctx, pdf_doc);
    fz_drop_context(ctx);

    printf("Done!\n");
    return EXIT_SUCCESS;
}

问题原因分析

你之前的代码核心缺失是:只创建了pixmap,但没有将pixmap的内容绘制到输出PDF的页面设备上。调用DrawPathOnPage时是直接在输出设备上绘制了路径,所以能显示内容;但pixmap的内容从未被传递到输出设备,因此PDF显示为空。

关键修改说明

  1. 修正页面尺寸获取:原代码错误地复用了Media Box的变量来打印Crop Box,现在改为正确获取原页面的Crop Box作为新页面的尺寸
  2. 添加输出页面设备:通过fz_new_pdf_page为输出PDF创建新页面并获取绘制设备
  3. 设置变换矩阵:确保pixmap能正确对齐到页面的左上角位置,适配原页面的Crop Box偏移
  4. 绘制pixmap到设备:调用fz_draw_pixmap将pixmap内容渲染到输出设备
  5. 完善资源清理:添加了统一的资源清理分支,避免内存泄漏

额外注意点

  • 如果你的pixmap使用了非72PPI的分辨率(比如96PPI),需要在变换矩阵中加入对应的缩放因子,保证pixmap尺寸和页面匹配
  • 所有muPDF创建的资源(pixmap、device、page等)必须用对应的fz_drop_*函数释放
  • 可以替换FZ_CROP_BOX为FZ_MEDIA_BOX,根据需求选择页面的基准尺寸

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 10:14:36