3 This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
5 Copyright (C) 2007-2012 by Jin-Hwan Cho and Shunsaku Hirata,
6 the dvipdfmx project team.
8 Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
32 * All bitdepth less than 16 is supported.
33 * Supported color types are: PALETTE, RGB, GRAY, RGB_ALPHA, GRAY_ALPHA.
34 * Supported ancillary chunks: tRNS, cHRM + gAMA, (sRGB), (iCCP)
36 * gAMA support is available only when cHRM exists. cHRM support is not
37 * tested well. CalRGB/CalGray colorspace is used for PNG images that
38 * have cHRM chunk (but not sRGB).
42 * Recent version of PDF (>= 1.5) support 16 bpc, but 16 bit bitdepth PNG
43 * images are automatically converted to 8 bit bitpedth image.
47 * sBIT ? iTXT, tEXT and tIME as MetaData ?, pHYS (see below)
48 * 16 bpc support for PDF-1.5. JBIG compression for monochrome image.
49 * Predictor for deflate ?
61 #define PNG_DEBUG_STR "PNG"
67 * Write, MNG, Progressive not required.
69 #define PNG_NO_WRITE_SUPPORTED
70 #define PNG_NO_MNG_FEATURES
71 #define PNG_NO_PROGRESSIVE_READ
73 /* 16_TO_8 required. */
74 #define PNG_NO_READ_TRANSFORMS
80 #include "pdfximage.h"
82 #define PDF_TRANS_TYPE_NONE 0
83 #define PDF_TRANS_TYPE_BINARY 1
84 #define PDF_TRANS_TYPE_ALPHA 2
87 static pdf_obj *create_cspace_Indexed (png_structp png_ptr, png_infop info_ptr);
89 /* CIE-Based: CalRGB/CalGray
91 * We ignore gAMA if cHRM is not found.
93 static pdf_obj *create_cspace_CalRGB (png_structp png_ptr, png_infop info_ptr);
94 static pdf_obj *create_cspace_CalGray (png_structp png_ptr, png_infop info_ptr);
95 static pdf_obj *make_param_Cal (png_byte color_type,
100 double xb, double yb);
104 * We (and PDF) do not have direct sRGB support. The sRGB color space can be
105 * precisely represented by ICC profile, but we use approximate CalRGB color
108 static pdf_obj *create_cspace_sRGB (png_structp png_ptr, png_infop info_ptr);
109 static pdf_obj *get_rendering_intent (png_structp png_ptr, png_infop info_ptr);
114 * Must check if ICC profile is valid and can be imported to PDF.
115 * There are few restrictions (should be applied to PNG too?) in ICC profile
116 * support in PDF. Some information should be obtained from profile.
118 static pdf_obj *create_cspace_ICCBased (png_structp png_ptr, png_infop info_ptr);
121 static int check_transparency (png_structp png_ptr, png_infop info_ptr);
123 static pdf_obj *create_ckey_mask (png_structp png_ptr, png_infop info_ptr);
126 * create_soft_mask() is for PNG_COLOR_TYPE_PALLETE.
127 * Images with alpha chunnel use strip_soft_mask().
128 * An object representing mask itself is returned.
130 static pdf_obj *create_soft_mask (png_structp png_ptr, png_infop info_ptr,
131 png_bytep image_data_ptr,
132 png_uint_32 width, png_uint_32 height);
133 static pdf_obj *strip_soft_mask (png_structp png_ptr, png_infop info_ptr,
134 png_bytep image_data_ptr,
135 png_uint_32p rowbytes_ptr,
136 png_uint_32 width, png_uint_32 height);
138 /* Read image body */
139 static void read_image_data (png_structp png_ptr, png_infop info_ptr,
141 png_uint_32 height, png_uint_32 rowbytes);
144 check_for_png (FILE *png_file)
146 unsigned char sigbytes[4];
149 if (fread (sigbytes, 1, sizeof(sigbytes), png_file) !=
151 (png_sig_cmp (sigbytes, 0, sizeof(sigbytes))))
158 png_include_image (pdf_ximage *ximage, FILE *png_file)
161 pdf_obj *stream_dict;
162 pdf_obj *colorspace, *mask, *intent;
163 png_bytep stream_data_ptr;
168 png_infop png_info_ptr;
169 png_byte bpc, color_type;
170 png_uint_32 width, height, rowbytes, xppm, yppm;
172 pdf_ximage_init_image_info(&info);
176 colorspace = mask = intent = NULL;
179 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
180 if (png_ptr == NULL ||
181 (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) {
182 WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR);
184 png_destroy_read_struct(&png_ptr, NULL, NULL);
188 /* Inititializing file IO. */
189 png_init_io (png_ptr, png_file);
191 /* Read PNG info-header and get some info. */
192 png_read_info(png_ptr, png_info_ptr);
193 color_type = png_get_color_type (png_ptr, png_info_ptr);
194 width = png_get_image_width (png_ptr, png_info_ptr);
195 height = png_get_image_height(png_ptr, png_info_ptr);
196 bpc = png_get_bit_depth (png_ptr, png_info_ptr);
197 xppm = png_get_x_pixels_per_meter(png_ptr, png_info_ptr);
198 yppm = png_get_y_pixels_per_meter(png_ptr, png_info_ptr);
200 /* We do not need 16-bpc color. Ask libpng to convert down to 8-bpc. */
202 png_set_strip_16(png_ptr);
206 trans_type = check_transparency(png_ptr, png_info_ptr);
207 /* check_transparency() does not do updata_info() */
208 png_read_update_info(png_ptr, png_info_ptr);
209 rowbytes = png_get_rowbytes(png_ptr, png_info_ptr);
211 /* Values listed below will not be modified in the remaining process. */
213 info.height = height;
214 info.bits_per_component = bpc;
217 info.xdensity = info.ydensity = 72.0 / 100.0;
220 info.xdensity = 72.0 / 0.0254 / xppm;
222 info.ydensity = 72.0 / 0.0254 / yppm;
225 stream = pdf_new_stream (STREAM_COMPRESS);
226 stream_dict = pdf_stream_dict(stream);
228 stream_data_ptr = (png_bytep) NEW(rowbytes*height, png_byte);
229 read_image_data(png_ptr, png_info_ptr, stream_data_ptr, height, rowbytes);
231 /* Non-NULL intent means there is valid sRGB chunk. */
232 intent = get_rendering_intent(png_ptr, png_info_ptr);
234 pdf_add_dict(stream_dict, pdf_new_name("Intent"), intent);
236 switch (color_type) {
237 case PNG_COLOR_TYPE_PALETTE:
239 colorspace = create_cspace_Indexed(png_ptr, png_info_ptr);
241 switch (trans_type) {
242 case PDF_TRANS_TYPE_BINARY:
243 /* Color-key masking */
244 mask = create_ckey_mask(png_ptr, png_info_ptr);
246 case PDF_TRANS_TYPE_ALPHA:
248 mask = create_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, width, height);
251 /* Nothing to be done here.
252 * No tRNS chunk or image already composited with background color.
257 case PNG_COLOR_TYPE_RGB:
258 case PNG_COLOR_TYPE_RGB_ALPHA:
260 if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP))
261 colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr);
263 colorspace = create_cspace_sRGB(png_ptr, png_info_ptr);
265 colorspace = create_cspace_CalRGB(png_ptr, png_info_ptr);
268 colorspace = pdf_new_name("DeviceRGB");
270 switch (trans_type) {
271 case PDF_TRANS_TYPE_BINARY:
272 if (color_type != PNG_COLOR_TYPE_RGB)
273 ERROR("Unexpected error in png_include_image().");
274 mask = create_ckey_mask(png_ptr, png_info_ptr);
276 /* rowbytes changes 4 to 3 at here */
277 case PDF_TRANS_TYPE_ALPHA:
278 if (color_type != PNG_COLOR_TYPE_RGB_ALPHA)
279 ERROR("Unexpected error in png_include_image().");
280 mask = strip_soft_mask(png_ptr, png_info_ptr,
281 stream_data_ptr, &rowbytes, width, height);
286 info.num_components = 3;
289 case PNG_COLOR_TYPE_GRAY:
290 case PNG_COLOR_TYPE_GRAY_ALPHA:
292 if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP))
293 colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr);
295 colorspace = create_cspace_sRGB(png_ptr, png_info_ptr);
297 colorspace = create_cspace_CalGray(png_ptr, png_info_ptr);
300 colorspace = pdf_new_name("DeviceGray");
302 switch (trans_type) {
303 case PDF_TRANS_TYPE_BINARY:
304 if (color_type != PNG_COLOR_TYPE_GRAY)
305 ERROR("Unexpected error in png_include_image().");
306 mask = create_ckey_mask(png_ptr, png_info_ptr);
308 case PDF_TRANS_TYPE_ALPHA:
309 if (color_type != PNG_COLOR_TYPE_GRAY_ALPHA)
310 ERROR("Unexpected error in png_include_image().");
311 mask = strip_soft_mask(png_ptr, png_info_ptr,
312 stream_data_ptr, &rowbytes, width, height);
317 info.num_components = 1;
321 WARN("%s: Unknown PNG colortype %d.", PNG_DEBUG_STR, color_type);
323 pdf_add_dict(stream_dict, pdf_new_name("ColorSpace"), colorspace);
325 pdf_add_stream(stream, stream_data_ptr, rowbytes*height);
326 RELEASE(stream_data_ptr);
329 if (trans_type == PDF_TRANS_TYPE_BINARY)
330 pdf_add_dict(stream_dict, pdf_new_name("Mask"), mask);
331 else if (trans_type == PDF_TRANS_TYPE_ALPHA) {
332 pdf_add_dict(stream_dict, pdf_new_name("SMask"), pdf_ref_obj(mask));
333 pdf_release_obj(mask);
335 WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
336 pdf_release_obj(mask);
340 png_read_end(png_ptr, NULL);
344 png_destroy_info_struct(png_ptr, &png_info_ptr);
346 png_destroy_read_struct(&png_ptr, NULL, NULL);
348 pdf_ximage_set_image(ximage, &info, stream);
354 * The returned value trans_type is the type of transparency to be used for
355 * this image. Possible values are:
357 * PDF_TRANS_TYPE_NONE No Masking will be used/required.
358 * PDF_TRANS_TYPE_BINARY Pixels are either fully opaque/fully transparent.
359 * PDF_TRANS_TYPE_ALPHA Uses alpha channel, requies SMask.(PDF-1.4)
361 * check_transparency() must check the current setting of output PDF version
362 * and must choose appropriate trans_type value according to PDF version of
363 * current output PDF document.
365 * If the PDF version is less than 1.3, no transparency is supported for this
366 * version of PDF, hence PDF_TRANS_TYPE_NONE must be returned. And when the PDF
367 * version is equal to 1.3, possible retrun values are PDF_TRANS_TYPE_BINARY or
368 * PDF_TRANS_TYPE_NONE. The latter case arises when PNG file uses alpha channel
369 * explicitly (color type PNG_COLOR_TYPE_XXX_ALPHA), or the tRNS chunk for the
370 * PNG_COLOR_TYPE_PALETTE image contains intermediate values of opacity.
372 * Finally, in the case of PDF version 1.4, all kind of translucent pixels can
373 * be represented with Soft-Mask.
377 check_transparency (png_structp png_ptr, png_infop info_ptr)
380 unsigned pdf_version;
382 png_color_16p trans_values;
386 pdf_version = pdf_get_version();
387 color_type = png_get_color_type(png_ptr, info_ptr);
390 * First we set trans_type to appropriate value for PNG image.
392 if (color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
393 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
394 trans_type = PDF_TRANS_TYPE_ALPHA;
395 } else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) &&
396 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values)) {
397 /* Have valid tRNS chunk. */
398 switch (color_type) {
399 case PNG_COLOR_TYPE_PALETTE:
400 /* Use color-key mask if possible. */
401 trans_type = PDF_TRANS_TYPE_BINARY;
402 while (num_trans-- > 0) {
403 if (trans[num_trans] != 0x00 && trans[num_trans] != 0xff) {
404 /* This seems not binary transparency */
405 trans_type = PDF_TRANS_TYPE_ALPHA;
410 case PNG_COLOR_TYPE_GRAY:
411 case PNG_COLOR_TYPE_RGB:
412 /* RGB or GRAY, single color specified by trans_values is transparent. */
413 trans_type = PDF_TRANS_TYPE_BINARY;
416 /* Else tRNS silently ignored. */
417 trans_type = PDF_TRANS_TYPE_NONE;
419 } else { /* no transparency */
420 trans_type = PDF_TRANS_TYPE_NONE;
424 * Now we check PDF version.
425 * We can convert alpha cahnnels to explicit mask via user supplied alpha-
426 * threshold value. But I will not do that.
428 if (( pdf_version < 3 && trans_type != PDF_TRANS_TYPE_NONE ) ||
429 ( pdf_version < 4 && trans_type == PDF_TRANS_TYPE_ALPHA )) {
431 * No transparency supported but PNG uses transparency, or Soft-Mask
432 * required but no support for it is available in this version of PDF.
433 * We must do pre-composition of image with the background image here. But,
434 * we cannot do that in general since dvipdfmx is not a rasterizer. What we
435 * can do here is to composite image with a rectangle filled with the
436 * background color. However, images are stored as an Image XObject which
437 * can be referenced anywhere in the PDF document content. Hence, we cannot
438 * know the correct background color at this time. So we will choose white
439 * as background color, which is most probable color in our cases.
440 * We ignore bKGD chunk.
443 bg.red = 255; bg.green = 255; bg.blue = 255; bg.gray = 255; bg.index = 0;
444 png_set_background(png_ptr, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
445 WARN("%s: Transparency will be ignored. (no support in PDF ver. < 1.3)", PNG_DEBUG_STR);
447 WARN("%s: Please use -V 3 option to enable binary transparency support.", PNG_DEBUG_STR);
449 WARN("%s: Please use -V 4 option to enable full alpha channel support.", PNG_DEBUG_STR);
450 trans_type = PDF_TRANS_TYPE_NONE;
459 * If sRGB chunk is present, cHRM and gAMA chunk must be ignored.
463 get_rendering_intent (png_structp png_ptr, png_infop info_ptr)
468 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB) &&
469 png_get_sRGB (png_ptr, info_ptr, &srgb_intent)) {
470 switch (srgb_intent) {
471 case PNG_sRGB_INTENT_SATURATION:
472 intent = pdf_new_name("Saturation");
474 case PNG_sRGB_INTENT_PERCEPTUAL:
475 intent = pdf_new_name("Perceptual");
477 case PNG_sRGB_INTENT_ABSOLUTE:
478 intent = pdf_new_name("AbsoluteColorimetric");
480 case PNG_sRGB_INTENT_RELATIVE:
481 intent = pdf_new_name("RelativeColorimetric");
484 WARN("%s: Invalid value in PNG sRGB chunk: %d", PNG_DEBUG_STR, srgb_intent);
493 /* Approximated sRGB */
495 create_cspace_sRGB (png_structp png_ptr, png_infop info_ptr)
501 color_type = png_get_color_type(png_ptr, info_ptr);
503 /* Parameters taken from PNG spec. section 4.2.2.3. */
504 cal_param = make_param_Cal(color_type,
507 0.64, 0.33, 0.3, 0.6, 0.15, 0.06);
511 colorspace = pdf_new_array();
513 switch (color_type) {
514 case PNG_COLOR_TYPE_RGB:
515 case PNG_COLOR_TYPE_RGB_ALPHA:
516 case PNG_COLOR_TYPE_PALETTE:
517 pdf_add_array(colorspace, pdf_new_name("CalRGB"));
519 case PNG_COLOR_TYPE_GRAY:
520 case PNG_COLOR_TYPE_GRAY_ALPHA:
521 pdf_add_array(colorspace, pdf_new_name("CalGray"));
524 pdf_add_array(colorspace, cal_param);
530 create_cspace_ICCBased (png_structp png_ptr, png_infop info_ptr)
533 int csp_id, colortype;
536 int compression_type; /* Manual page for libpng does not
537 * clarify whether profile data is inflated by libpng.
539 #if PNG_LIBPNG_VER_MINOR < 5
546 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP) ||
547 !png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile, &proflen))
550 color_type = png_get_color_type(png_ptr, info_ptr);
552 if (color_type & PNG_COLOR_MASK_COLOR) {
553 colortype = PDF_COLORSPACE_TYPE_RGB;
555 alternate = create_cspace_CalRGB(png_ptr, info_ptr);
558 colortype = PDF_COLORSPACE_TYPE_GRAY;
560 alternate = create_cspace_CalGray(png_ptr, info_ptr);
566 pdf_add_dict(dict, pdf_new_name("Alternate"), alternate);
569 if (iccp_check_colorspace(colortype, profile, proflen) < 0)
572 csp_id = iccp_load_profile(name, profile, proflen);
576 colorspace = pdf_get_colorspace_reference(csp_id);
580 /* Rendering intent ... */
588 * If cHRM is present, we use CIE-Based color space. gAMA is also used here
592 #define INVALID_CHRM_VALUE(xw,yw,xr,yr,xg,yg,xb,yb) (\
593 (xw) <= 0.0 || (yw) < 1.0e-10 || \
594 (xr) < 0.0 || (yr) < 0.0 || (xg) < 0.0 || (yg) < 0.0 || \
595 (xb) < 0.0 || (yb) < 0.0)
598 create_cspace_CalRGB (png_structp png_ptr, png_infop info_ptr)
602 double xw, yw, xr, yr, xg, yg, xb, yb;
605 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM) ||
606 !png_get_cHRM(png_ptr, info_ptr, &xw, &yw, &xr, &yr, &xg, &yg, &xb, &yb))
609 if (xw <= 0.0 || yw < 1.0e-10 ||
610 xr < 0.0 || yr < 0.0 || xg < 0.0 || yg < 0.0 || xb < 0.0 || yb < 0.0) {
611 WARN("%s: Invalid cHRM chunk parameters found.", PNG_DEBUG_STR);
615 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
616 png_get_gAMA (png_ptr, info_ptr, &G)) {
618 WARN("%s: Unusual Gamma value: %g", PNG_DEBUG_STR, G);
621 G = 1.0 / G; /* Gamma is inverted. */
626 cal_param = make_param_Cal(PNG_COLOR_TYPE_RGB, G, xw, yw, xr, yr, xg, yg, xb, yb);
631 colorspace = pdf_new_array();
632 pdf_add_array(colorspace, pdf_new_name("CalRGB"));
633 pdf_add_array(colorspace, cal_param);
639 create_cspace_CalGray (png_structp png_ptr, png_infop info_ptr)
643 double xw, yw, xr, yr, xg, yg, xb, yb;
646 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM) ||
647 !png_get_cHRM(png_ptr, info_ptr, &xw, &yw, &xr, &yr, &xg, &yg, &xb, &yb))
650 if (xw <= 0.0 || yw < 1.0e-10 ||
651 xr < 0.0 || yr < 0.0 || xg < 0.0 || yg < 0.0 || xb < 0.0 || yb < 0.0) {
652 WARN("%s: Invalid cHRM chunk parameters found.", PNG_DEBUG_STR);
656 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
657 png_get_gAMA (png_ptr, info_ptr, &G)) {
659 WARN("%s: Unusual Gamma value: %g", PNG_DEBUG_STR, G);
662 G = 1.0 / G; /* Gamma is inverted. */
667 cal_param = make_param_Cal(PNG_COLOR_TYPE_GRAY, G, xw, yw, xr, yr, xg, yg, xb, yb);
672 colorspace = pdf_new_array();
673 pdf_add_array(colorspace, pdf_new_name("CalGray"));
674 pdf_add_array(colorspace, cal_param);
680 make_param_Cal (png_byte color_type,
681 double G, /* Gamma */
682 double xw, double yw,
683 double xr, double yr, double xg, double yg, double xb, double yb)
686 pdf_obj *white_point, *matrix, *dev_gamma;
687 double Xw, Yw, Zw; /* Yw = 1.0 */
688 double Xr, Xg, Xb, Yr, Yb, Yg, Zr, Zg, Zb;
691 #define ABS(x) ((x) < 0 ? -(x) : (x))
694 * TODO: Check validity
696 * Conversion found in
698 * com.sixlegs.image.png - Java package to read and display PNG images
699 * Copyright (C) 1998, 1999, 2001 Chris Nokleberg
701 * http://www.sixlegs.com/software/png/
705 double zw, zr, zg, zb;
711 zr = 1 - (xr + yr); zg = 1 - (xg + yg); zb = 1 - (xb + yb);
712 Xw = xw / yw; Yw = 1.0; Zw = zw / yw;
715 det = xr * (yg * zb - zg * yb) - xg * (yr * zb - zr * yb) + xb * (yr * zg - zr * yg);
716 if (ABS(det) < 1.0e-10) {
717 WARN("Non invertible matrix: Maybe invalid value(s) specified in cHRM chunk.");
720 fr = (Xw * (yg * zb - zg * yb) - xg * (zb - Zw * yb) + xb * (zg - Zw * yg)) / det;
721 fg = (xr * (zb - Zw * yb) - Xw * (yr * zb - zr * yb) + xb * (yr * Zw - zr)) / det;
722 fb = (xr * (yg * Zw - zg) - xg * (yr * Zw - zr) + Xw * (yr * zg - zr * yg)) / det;
723 Xr = fr * xr; Yr = fr * yr; Zr = fr * zr;
724 Xg = fg * xg; Yg = fg * yg; Zg = fg * zg;
725 Xb = fb * xb; Yb = fb * yb; Zb = fb * zb;
729 WARN("Unusual Gamma specified: %g", G);
733 cal_param = pdf_new_dict();
735 /* White point is always required. */
736 white_point = pdf_new_array();
737 pdf_add_array(white_point, pdf_new_number(ROUND(Xw, 0.00001)));
738 pdf_add_array(white_point, pdf_new_number(ROUND(Yw, 0.00001)));
739 pdf_add_array(white_point, pdf_new_number(ROUND(Zw, 0.00001)));
740 pdf_add_dict(cal_param, pdf_new_name("WhitePoint"), white_point);
742 /* Matrix - default: Identity */
743 if (color_type & PNG_COLOR_MASK_COLOR) {
745 dev_gamma = pdf_new_array();
746 pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
747 pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
748 pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
749 pdf_add_dict(cal_param, pdf_new_name("Gamma"), dev_gamma);
752 matrix = pdf_new_array();
753 pdf_add_array(matrix, pdf_new_number(ROUND(Xr, 0.00001)));
754 pdf_add_array(matrix, pdf_new_number(ROUND(Yr, 0.00001)));
755 pdf_add_array(matrix, pdf_new_number(ROUND(Zr, 0.00001)));
756 pdf_add_array(matrix, pdf_new_number(ROUND(Xg, 0.00001)));
757 pdf_add_array(matrix, pdf_new_number(ROUND(Yg, 0.00001)));
758 pdf_add_array(matrix, pdf_new_number(ROUND(Zg, 0.00001)));
759 pdf_add_array(matrix, pdf_new_number(ROUND(Xb, 0.00001)));
760 pdf_add_array(matrix, pdf_new_number(ROUND(Yb, 0.00001)));
761 pdf_add_array(matrix, pdf_new_number(ROUND(Zb, 0.00001)));
762 pdf_add_dict (cal_param, pdf_new_name("Matrix"), matrix);
765 pdf_add_dict(cal_param,
766 pdf_new_name("Gamma"),
767 pdf_new_number(ROUND(G, 0.00001)));
774 * Set up Indexed ColorSpace for color-type PALETTE:
776 * PNG allows only RGB color for base color space. If gAMA and/or cHRM
777 * chunk is available, we can use CalRGB color space instead of DeviceRGB
778 * for base color space.
782 create_cspace_Indexed (png_structp png_ptr, png_infop info_ptr)
785 pdf_obj *base, *lookup;
790 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE) ||
791 !png_get_PLTE(png_ptr, info_ptr, &plte, &num_plte)) {
792 WARN("%s: PNG does not have valid PLTE chunk.", PNG_DEBUG_STR);
796 /* Order is important. */
797 colorspace = pdf_new_array ();
798 pdf_add_array(colorspace, pdf_new_name("Indexed"));
800 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP))
801 base = create_cspace_ICCBased(png_ptr, info_ptr);
803 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB))
804 base = create_cspace_sRGB(png_ptr, info_ptr);
806 base = create_cspace_CalRGB(png_ptr, info_ptr);
810 base = pdf_new_name("DeviceRGB");
812 pdf_add_array(colorspace, base);
813 pdf_add_array(colorspace, pdf_new_number(num_plte-1));
814 data_ptr = NEW(num_plte*3, png_byte);
815 for (i = 0; i < num_plte; i++) {
816 data_ptr[3*i] = plte[i].red;
817 data_ptr[3*i+1] = plte[i].green;
818 data_ptr[3*i+2] = plte[i].blue;
820 lookup = pdf_new_string(data_ptr, num_plte*3);
822 pdf_add_array(colorspace, lookup);
830 * pngimage.c is not responsible for adjusting image size.
831 * Higher layer must do something for this.
835 * Colorkey Mask: array
837 * [component_0_min component_0_max ... component_n_min component_n_max]
842 create_ckey_mask (png_structp png_ptr, png_infop info_ptr)
848 png_color_16p colors;
850 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
851 !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &colors)) {
852 WARN("%s: PNG does not have valid tRNS chunk!", PNG_DEBUG_STR);
856 colorkeys = pdf_new_array();
857 color_type = png_get_color_type(png_ptr, info_ptr);
859 switch (color_type) {
860 case PNG_COLOR_TYPE_PALETTE:
861 for (i = 0; i < num_trans; i++) {
862 if (trans[i] == 0x00) {
863 pdf_add_array(colorkeys, pdf_new_number(i));
864 pdf_add_array(colorkeys, pdf_new_number(i));
865 } else if (trans[i] != 0xff) {
866 WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
870 case PNG_COLOR_TYPE_RGB:
871 pdf_add_array(colorkeys, pdf_new_number(colors->red));
872 pdf_add_array(colorkeys, pdf_new_number(colors->red));
873 pdf_add_array(colorkeys, pdf_new_number(colors->green));
874 pdf_add_array(colorkeys, pdf_new_number(colors->green));
875 pdf_add_array(colorkeys, pdf_new_number(colors->blue));
876 pdf_add_array(colorkeys, pdf_new_number(colors->blue));
878 case PNG_COLOR_TYPE_GRAY:
879 pdf_add_array(colorkeys, pdf_new_number(colors->gray));
880 pdf_add_array(colorkeys, pdf_new_number(colors->gray));
883 WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
884 pdf_release_obj(colorkeys);
899 * /BitsPerComponent bpc
901 * stream .... endstream
903 * ColorSpace, Mask, SMask must be absent. ImageMask must be false or absent.
907 create_soft_mask (png_structp png_ptr, png_infop info_ptr,
908 png_bytep image_data_ptr, png_uint_32 width, png_uint_32 height)
910 pdf_obj *smask, *dict;
911 png_bytep smask_data_ptr;
916 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
917 !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL)) {
918 WARN("%s: PNG does not have valid tRNS chunk but tRNS is requested.", PNG_DEBUG_STR);
922 smask = pdf_new_stream(STREAM_COMPRESS);
923 dict = pdf_stream_dict(smask);
924 smask_data_ptr = (png_bytep) NEW(width*height, png_byte);
925 pdf_add_dict(dict, pdf_new_name("Type"), pdf_new_name("XObjcect"));
926 pdf_add_dict(dict, pdf_new_name("Subtype"), pdf_new_name("Image"));
927 pdf_add_dict(dict, pdf_new_name("Width"), pdf_new_number(width));
928 pdf_add_dict(dict, pdf_new_name("Height"), pdf_new_number(height));
929 pdf_add_dict(dict, pdf_new_name("ColorSpace"), pdf_new_name("DeviceGray"));
930 pdf_add_dict(dict, pdf_new_name("BitsPerComponent"), pdf_new_number(8));
931 for (i = 0; i < width*height; i++) {
932 png_byte idx = image_data_ptr[i];
933 smask_data_ptr[i] = (idx < num_trans) ? trans[idx] : 0xff;
935 pdf_add_stream(smask, (char *)smask_data_ptr, width*height);
936 RELEASE(smask_data_ptr);
941 /* bitdepth is always 8 (16 is not supported) */
943 strip_soft_mask (png_structp png_ptr, png_infop info_ptr,
944 /* next two values will be modified. */
945 png_bytep image_data_ptr, png_uint_32p rowbytes_ptr,
946 png_uint_32 width, png_uint_32 height)
948 pdf_obj *smask, *dict;
950 png_bytep smask_data_ptr;
953 color_type = png_get_color_type(png_ptr, info_ptr);
955 if (color_type & PNG_COLOR_MASK_COLOR) {
956 if (*rowbytes_ptr != 4*width*sizeof(png_byte)) { /* Something wrong */
957 WARN("%s: Inconsistent rowbytes value.", PNG_DEBUG_STR);
961 if (*rowbytes_ptr != 2*width*sizeof(png_byte)) { /* Something wrong */
962 WARN("%s: Inconsistent rowbytes value.", PNG_DEBUG_STR);
967 smask = pdf_new_stream(STREAM_COMPRESS);
968 dict = pdf_stream_dict(smask);
969 pdf_add_dict(dict, pdf_new_name("Type"), pdf_new_name("XObjcect"));
970 pdf_add_dict(dict, pdf_new_name("Subtype"), pdf_new_name("Image"));
971 pdf_add_dict(dict, pdf_new_name("Width"), pdf_new_number(width));
972 pdf_add_dict(dict, pdf_new_name("Height"), pdf_new_number(height));
973 pdf_add_dict(dict, pdf_new_name("ColorSpace"), pdf_new_name("DeviceGray"));
974 pdf_add_dict(dict, pdf_new_name("BitsPerComponent"), pdf_new_number(8));
976 smask_data_ptr = (png_bytep) NEW(width*height, png_byte);
978 switch (color_type) {
979 case PNG_COLOR_TYPE_RGB_ALPHA:
980 for (i = 0; i < width*height; i++) {
981 memmove(image_data_ptr+(3*i), image_data_ptr+(4*i), 3);
982 smask_data_ptr[i] = image_data_ptr[4*i+3];
984 *rowbytes_ptr = 3*width*sizeof(png_byte);
986 case PNG_COLOR_TYPE_GRAY_ALPHA:
987 for (i = 0; i < width*height; i++) {
988 image_data_ptr[i] = image_data_ptr[2*i];
989 smask_data_ptr[i] = image_data_ptr[2*i+1];
991 *rowbytes_ptr = width*sizeof(png_byte);
994 WARN("You found a bug in pngimage.c!");
995 pdf_release_obj(smask);
996 RELEASE(smask_data_ptr);
1000 pdf_add_stream(smask, smask_data_ptr, width*height);
1001 RELEASE(smask_data_ptr);
1007 read_image_data (png_structp png_ptr, png_infop info_ptr, /* info_ptr unused */
1008 png_bytep dest_ptr, png_uint_32 height, png_uint_32 rowbytes)
1013 rows_p = (png_bytepp) NEW (height, png_bytep);
1014 for (i=0; i< height; i++)
1015 rows_p[i] = dest_ptr + (rowbytes * i);
1016 png_read_image(png_ptr, rows_p);
1021 png_get_bbox (FILE *png_file, long *width, long *height,
1022 double *xdensity, double *ydensity)
1024 png_structp png_ptr;
1025 png_infop png_info_ptr;
1026 png_uint_32 xppm, yppm;
1029 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1030 if (png_ptr == NULL ||
1031 (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) {
1032 WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR);
1034 png_destroy_read_struct(&png_ptr, NULL, NULL);
1038 /* Inititializing file IO. */
1039 png_init_io (png_ptr, png_file);
1041 /* Read PNG info-header and get some info. */
1042 png_read_info(png_ptr, png_info_ptr);
1043 *width = png_get_image_width (png_ptr, png_info_ptr);
1044 *height = png_get_image_height(png_ptr, png_info_ptr);
1045 xppm = png_get_x_pixels_per_meter(png_ptr, png_info_ptr);
1046 yppm = png_get_y_pixels_per_meter(png_ptr, png_info_ptr);
1050 png_destroy_info_struct(png_ptr, &png_info_ptr);
1052 png_destroy_read_struct(&png_ptr, NULL, NULL);
1055 *xdensity = *ydensity = 72.0 / 100.0;
1057 *xdensity = xppm ? 72.0 / 0.0254 / xppm : 1.0;
1058 *ydensity = yppm ? 72.0 / 0.0254 / yppm : 1.0;
1064 #endif /* HAVE_LIBPNG */