cpdfmupdf

Why muPDF/C outputs an empty page after output of rendered pixmap?


I have written a program which reads a page cur_page_number = 0 from input file, converts it to a pixmap pix and writes it to an output PDF file, using device out_device.
The pixmap saved in PNG looks right.
PDF file looks as empty (or transparent ???). If I uncomment a call of DrawPathOnPage(ctx, out_device); (fills a rectangle and outputs it on a page), then output PDF file will contain the red filled rectangle, so, output_dev is working good.
Why does it occur (why a viewer doesn't see a pixmap in output PDF file and shows empty page in it) ?

The code is:

#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);
    }

    //fz_rect mediabox_crop;
    //fz_matrix ctm_crop;
    //pdf_page_transform_box(ctx, page, &mediabox_crop, &ctm_crop, FZ_CROP_BOX);

    // 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);
    print_rect("   :CROP  BOX: ", mediabox_media);

    int ppi = 72;

    float scale_factor = (float)ppi / 72.0;
    //ctm = fz_scale(scale_factor, scale_factor);

    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); } 

    //Form filename for PNG
    int written_chars = sprintf_s(output_png_fname, sizeof(output_png_fname), output_png_fname_mask, cur_page_number);
    assert(written_chars > 0);

    fz_try(ctx)
        fz_save_pixmap_as_png(ctx, pix, output_png_fname);
    fz_catch(ctx) {
        assert(0);
    }

    // Starts new page
    fz_device *out_device = nullptr;    // output device to write a content of new page

    // get pixmap (png) size
    // set size of the new PDF 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); }

    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);

    //float color_fo_fill[3] = {0.0f, 0.0f, 1.0f};
    //fz_fill_pixmap_with_color(ctx, pix, fz_device_rgb(ctx), color_fo_fill, fz_default_color_params);

    fz_matrix img_ctm = fz_scale(1, 1);
    fz_image *image = fz_new_image_from_pixmap(ctx, pix, NULL);

    // draw a path
    //DrawPathOnPage(ctx, out_device);  -- works good (outputs filled rectangle, if uncommented)

    // write an image to the device
    fz_fill_image(ctx, out_device, image, img_ctm, 1, 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_try(ctx)
        fz_close_document_writer(ctx, w);
    fz_catch(ctx) { assert(0); }

    // Drop the writer
    fz_try(ctx)
        fz_drop_document_writer(ctx, w);
    fz_catch(ctx) { assert(0); }

    /* Clean up. */
    fz_drop_pixmap(ctx, pix);

    pdf_drop_document(ctx, pdf_doc);
    fz_drop_context(ctx);
    return EXIT_SUCCESS;
}

Contents of out.pdf:

1 0 obj
<</Type/Catalog/Pages 2 0 R>>
endobj


2 0 obj
<</Type/Pages/Count 1/Kids[9 0 R]>>
endobj


3 0 obj
<</Type/XObject/Subtype/Image/DecodeParms<<>>/Width 596/Height 842/BitsPerComponent 8/ColorSpace 5 0 R/Length 1505496>>

stream
 ... endstream

endobj


4 0 obj
<</Length 2576/N 3/Alternate/DeviceRGB>>

stream ... endstream

endobj


5 0 obj
[/ICCBased 4 0 R]
endobj


6 0 obj
<</ca 0>>
endobj


7 0 obj
<</ExtGState<</Alp0 6 0 R>>/XObject<</Img3 3 0 R>>>>
endobj


8 0 obj
<</Length 53>>
stream
1 0 0 -1 -0 842 cm
/Alp0 gs
1 0 0 -1 0 1 cm
/Img3 Do


endstream
endobj


9 0 obj
<</Type/Page
/MediaBox[0 0 596 842]
/Rotate 0
/Resources 7 0 R
/Contents 8 0 R
/Parent 2 0 R>>

endobj


xref
0 10
0000000000 65535 f 
0000000016 00000 n 
0000000062 00000 n 
0000000114 00000 n 

0001505764 00000 n 
0001508415 00000 n 
0001508449 00000 n 
0001508475 00000 n 
0001508544 00000 n 
0001508646 00000 n 

trailer
<</Size 10/Root 1 0 R>>

startxref
1508752

%%EOF

Solution

  • The comment "PDF file looks as empty (or transparent ???)", is correct since on emulating the provided Output.PDF. I can confirm the image is present but nonvisible.

    The reason is the innocent looking /Alp0 definition:

    6 0 obj
    <</ca 0>>
    endobj
    

    Which when applied to contents as second line in the stream:

    8 0 obj
    <</Length 53>>
    stream
    1 0 0 -1 -0 842 cm
    /Alp0 gs
    1 0 0 -1 0 1 cm
    /Img3 Do
    

    Means the following objects have zero opacity (i.e. gs = ca 0 = transparent)

    So that is the first issue however even set to 1 (fully opaque) the page will still look as if nothing is rendered there.

    Thus, the secondary problem is the current size matrix (cm) before the image placement is set by default to equal one printer's point.

    1 0 0 -1 0 1 cm
    

    In my emulation that is top left. enter image description here enter image description here

    So which part of code is wrong?

    Alpha is set via

    int alpha = 0;
    

    This is incorrect in 2 ways as alpha is a "float" and actually means opacity = 0! The corrected code should be

    float alpha = 1.0f;
    

    The second correction is the use of:

        fz_matrix img_ctm = fz_scale(1, 1);
    

    Where inside a PDF there is no relative scale of images. There is NO DPI. Thus, assuming the image as if it is 1 ppp (pixel per point) is not correct. The placement is pure units (in this case only points).

    The correct definition could be:

    fz_matrix img_ctm = fz_scale(placement_width, placement_height);
    

    Where you define the fact it matches intended media size. As it stands your code is working other way round letting image determine page size at 1:1 so media is expanded to 596x842 (should be 595 wide)