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