summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipdfm-x/src/pngimage.c
diff options
context:
space:
mode:
authorPeter Breitenlohner <peb@mppmu.mpg.de>2013-06-24 09:51:24 +0000
committerPeter Breitenlohner <peb@mppmu.mpg.de>2013-06-24 09:51:24 +0000
commit4ce8c30e7e371dbecdfd21ac4f7fac2361aab384 (patch)
treef5c7f07d472660d1658ece66b418a8c6538c5537 /Build/source/texk/dvipdfm-x/src/pngimage.c
parente82a4ec6e8f06840867512112128041581c022e7 (diff)
Merged dvipdfmx + xdvipdfmx => dvipdfm-x
git-svn-id: svn://tug.org/texlive/trunk@30894 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/dvipdfm-x/src/pngimage.c')
-rw-r--r--Build/source/texk/dvipdfm-x/src/pngimage.c1064
1 files changed, 1064 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm-x/src/pngimage.c b/Build/source/texk/dvipdfm-x/src/pngimage.c
new file mode 100644
index 00000000000..2a835ae49f9
--- /dev/null
+++ b/Build/source/texk/dvipdfm-x/src/pngimage.c
@@ -0,0 +1,1064 @@
+/*
+
+ This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
+
+ Copyright (C) 2007-2012 by Jin-Hwan Cho and Shunsaku Hirata,
+ the dvipdfmx project team.
+
+ Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+ * PNG SUPPORT
+ *
+ * All bitdepth less than 16 is supported.
+ * Supported color types are: PALETTE, RGB, GRAY, RGB_ALPHA, GRAY_ALPHA.
+ * Supported ancillary chunks: tRNS, cHRM + gAMA, (sRGB), (iCCP)
+ *
+ * gAMA support is available only when cHRM exists. cHRM support is not
+ * tested well. CalRGB/CalGray colorspace is used for PNG images that
+ * have cHRM chunk (but not sRGB).
+ *
+ * LIMITATIONS
+ *
+ * Recent version of PDF (>= 1.5) support 16 bpc, but 16 bit bitdepth PNG
+ * images are automatically converted to 8 bit bitpedth image.
+ *
+ * TODO
+ *
+ * sBIT ? iTXT, tEXT and tIME as MetaData ?, pHYS (see below)
+ * 16 bpc support for PDF-1.5. JBIG compression for monochrome image.
+ * Predictor for deflate ?
+ */
+
+#include "system.h"
+#include "error.h"
+#include "mem.h"
+
+#include "dvipdfmx.h"
+
+#include "pdfcolor.h"
+#include "pdfobj.h"
+
+#define PNG_DEBUG_STR "PNG"
+#define PNG_DEBUG 3
+
+#ifdef HAVE_LIBPNG
+
+/*
+ * Write, MNG, Progressive not required.
+ */
+#define PNG_NO_WRITE_SUPPORTED
+#define PNG_NO_MNG_FEATURES
+#define PNG_NO_PROGRESSIVE_READ
+#if 0
+/* 16_TO_8 required. */
+#define PNG_NO_READ_TRANSFORMS
+#endif
+
+#include <png.h>
+#include "pngimage.h"
+
+#include "pdfximage.h"
+
+#define PDF_TRANS_TYPE_NONE 0
+#define PDF_TRANS_TYPE_BINARY 1
+#define PDF_TRANS_TYPE_ALPHA 2
+
+/* ColorSpace */
+static pdf_obj *create_cspace_Indexed (png_structp png_ptr, png_infop info_ptr);
+
+/* CIE-Based: CalRGB/CalGray
+ *
+ * We ignore gAMA if cHRM is not found.
+ */
+static pdf_obj *create_cspace_CalRGB (png_structp png_ptr, png_infop info_ptr);
+static pdf_obj *create_cspace_CalGray (png_structp png_ptr, png_infop info_ptr);
+static pdf_obj *make_param_Cal (png_byte color_type,
+ double G,
+ double xw, double yw,
+ double xr, double yr,
+ double xg, double yg,
+ double xb, double yb);
+
+/* sRGB:
+ *
+ * We (and PDF) do not have direct sRGB support. The sRGB color space can be
+ * precisely represented by ICC profile, but we use approximate CalRGB color
+ * space.
+ */
+static pdf_obj *create_cspace_sRGB (png_structp png_ptr, png_infop info_ptr);
+static pdf_obj *get_rendering_intent (png_structp png_ptr, png_infop info_ptr);
+
+/* ICCBased:
+ *
+ * Not supported yet.
+ * Must check if ICC profile is valid and can be imported to PDF.
+ * There are few restrictions (should be applied to PNG too?) in ICC profile
+ * support in PDF. Some information should be obtained from profile.
+ */
+static pdf_obj *create_cspace_ICCBased (png_structp png_ptr, png_infop info_ptr);
+
+/* Transparency */
+static int check_transparency (png_structp png_ptr, png_infop info_ptr);
+/* Color-Key Mask */
+static pdf_obj *create_ckey_mask (png_structp png_ptr, png_infop info_ptr);
+/* Soft Mask:
+ *
+ * create_soft_mask() is for PNG_COLOR_TYPE_PALLETE.
+ * Images with alpha chunnel use strip_soft_mask().
+ * An object representing mask itself is returned.
+ */
+static pdf_obj *create_soft_mask (png_structp png_ptr, png_infop info_ptr,
+ png_bytep image_data_ptr,
+ png_uint_32 width, png_uint_32 height);
+static pdf_obj *strip_soft_mask (png_structp png_ptr, png_infop info_ptr,
+ png_bytep image_data_ptr,
+ png_uint_32p rowbytes_ptr,
+ png_uint_32 width, png_uint_32 height);
+
+/* Read image body */
+static void read_image_data (png_structp png_ptr, png_infop info_ptr,
+ png_bytep dest_ptr,
+ png_uint_32 height, png_uint_32 rowbytes);
+
+int
+check_for_png (FILE *png_file)
+{
+ unsigned char sigbytes[4];
+
+ rewind (png_file);
+ if (fread (sigbytes, 1, sizeof(sigbytes), png_file) !=
+ sizeof(sigbytes) ||
+ (png_sig_cmp (sigbytes, 0, sizeof(sigbytes))))
+ return 0;
+ else
+ return 1;
+}
+
+int
+png_include_image (pdf_ximage *ximage, FILE *png_file)
+{
+ pdf_obj *stream;
+ pdf_obj *stream_dict;
+ pdf_obj *colorspace, *mask, *intent;
+ png_bytep stream_data_ptr;
+ int trans_type;
+ ximage_info info;
+ /* Libpng stuff */
+ png_structp png_ptr;
+ png_infop png_info_ptr;
+ png_byte bpc, color_type;
+ png_uint_32 width, height, rowbytes, xppm, yppm;
+
+ pdf_ximage_init_image_info(&info);
+
+ stream = NULL;
+ stream_dict = NULL;
+ colorspace = mask = intent = NULL;
+
+ rewind (png_file);
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png_ptr == NULL ||
+ (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) {
+ WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR);
+ if (png_ptr)
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return -1;
+ }
+
+ /* Inititializing file IO. */
+ png_init_io (png_ptr, png_file);
+
+ /* Read PNG info-header and get some info. */
+ png_read_info(png_ptr, png_info_ptr);
+ color_type = png_get_color_type (png_ptr, png_info_ptr);
+ width = png_get_image_width (png_ptr, png_info_ptr);
+ height = png_get_image_height(png_ptr, png_info_ptr);
+ bpc = png_get_bit_depth (png_ptr, png_info_ptr);
+ xppm = png_get_x_pixels_per_meter(png_ptr, png_info_ptr);
+ yppm = png_get_y_pixels_per_meter(png_ptr, png_info_ptr);
+
+ /* We do not need 16-bpc color. Ask libpng to convert down to 8-bpc. */
+ if (bpc > 8) {
+ png_set_strip_16(png_ptr);
+ bpc = 8;
+ }
+
+ trans_type = check_transparency(png_ptr, png_info_ptr);
+ /* check_transparency() does not do updata_info() */
+ png_read_update_info(png_ptr, png_info_ptr);
+ rowbytes = png_get_rowbytes(png_ptr, png_info_ptr);
+
+ /* Values listed below will not be modified in the remaining process. */
+ info.width = width;
+ info.height = height;
+ info.bits_per_component = bpc;
+
+ if (compat_mode)
+ info.xdensity = info.ydensity = 72.0 / 100.0;
+ else {
+ if (xppm > 0)
+ info.xdensity = 72.0 / 0.0254 / xppm;
+ if (yppm > 0)
+ info.ydensity = 72.0 / 0.0254 / yppm;
+ }
+
+ stream = pdf_new_stream (STREAM_COMPRESS);
+ stream_dict = pdf_stream_dict(stream);
+
+ stream_data_ptr = (png_bytep) NEW(rowbytes*height, png_byte);
+ read_image_data(png_ptr, png_info_ptr, stream_data_ptr, height, rowbytes);
+
+ /* Non-NULL intent means there is valid sRGB chunk. */
+ intent = get_rendering_intent(png_ptr, png_info_ptr);
+ if (intent)
+ pdf_add_dict(stream_dict, pdf_new_name("Intent"), intent);
+
+ switch (color_type) {
+ case PNG_COLOR_TYPE_PALETTE:
+
+ colorspace = create_cspace_Indexed(png_ptr, png_info_ptr);
+
+ switch (trans_type) {
+ case PDF_TRANS_TYPE_BINARY:
+ /* Color-key masking */
+ mask = create_ckey_mask(png_ptr, png_info_ptr);
+ break;
+ case PDF_TRANS_TYPE_ALPHA:
+ /* Soft mask */
+ mask = create_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, width, height);
+ break;
+ default:
+ /* Nothing to be done here.
+ * No tRNS chunk or image already composited with background color.
+ */
+ break;
+ }
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+
+ if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP))
+ colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr);
+ else if (intent) {
+ colorspace = create_cspace_sRGB(png_ptr, png_info_ptr);
+ } else {
+ colorspace = create_cspace_CalRGB(png_ptr, png_info_ptr);
+ }
+ if (!colorspace)
+ colorspace = pdf_new_name("DeviceRGB");
+
+ switch (trans_type) {
+ case PDF_TRANS_TYPE_BINARY:
+ if (color_type != PNG_COLOR_TYPE_RGB)
+ ERROR("Unexpected error in png_include_image().");
+ mask = create_ckey_mask(png_ptr, png_info_ptr);
+ break;
+ /* rowbytes changes 4 to 3 at here */
+ case PDF_TRANS_TYPE_ALPHA:
+ if (color_type != PNG_COLOR_TYPE_RGB_ALPHA)
+ ERROR("Unexpected error in png_include_image().");
+ mask = strip_soft_mask(png_ptr, png_info_ptr,
+ stream_data_ptr, &rowbytes, width, height);
+ break;
+ default:
+ mask = NULL;
+ }
+ info.num_components = 3;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY:
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+
+ if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP))
+ colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr);
+ else if (intent) {
+ colorspace = create_cspace_sRGB(png_ptr, png_info_ptr);
+ } else {
+ colorspace = create_cspace_CalGray(png_ptr, png_info_ptr);
+ }
+ if (!colorspace)
+ colorspace = pdf_new_name("DeviceGray");
+
+ switch (trans_type) {
+ case PDF_TRANS_TYPE_BINARY:
+ if (color_type != PNG_COLOR_TYPE_GRAY)
+ ERROR("Unexpected error in png_include_image().");
+ mask = create_ckey_mask(png_ptr, png_info_ptr);
+ break;
+ case PDF_TRANS_TYPE_ALPHA:
+ if (color_type != PNG_COLOR_TYPE_GRAY_ALPHA)
+ ERROR("Unexpected error in png_include_image().");
+ mask = strip_soft_mask(png_ptr, png_info_ptr,
+ stream_data_ptr, &rowbytes, width, height);
+ break;
+ default:
+ mask = NULL;
+ }
+ info.num_components = 1;
+ break;
+
+ default:
+ WARN("%s: Unknown PNG colortype %d.", PNG_DEBUG_STR, color_type);
+ }
+ pdf_add_dict(stream_dict, pdf_new_name("ColorSpace"), colorspace);
+
+ pdf_add_stream(stream, stream_data_ptr, rowbytes*height);
+ RELEASE(stream_data_ptr);
+
+ if (mask) {
+ if (trans_type == PDF_TRANS_TYPE_BINARY)
+ pdf_add_dict(stream_dict, pdf_new_name("Mask"), mask);
+ else if (trans_type == PDF_TRANS_TYPE_ALPHA) {
+ pdf_add_dict(stream_dict, pdf_new_name("SMask"), pdf_ref_obj(mask));
+ pdf_release_obj(mask);
+ } else {
+ WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
+ pdf_release_obj(mask);
+ }
+ }
+
+ png_read_end(png_ptr, NULL);
+
+ /* Cleanup */
+ if (png_info_ptr)
+ png_destroy_info_struct(png_ptr, &png_info_ptr);
+ if (png_ptr)
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+
+ pdf_ximage_set_image(ximage, &info, stream);
+
+ return 0;
+}
+
+/*
+ * The returned value trans_type is the type of transparency to be used for
+ * this image. Possible values are:
+ *
+ * PDF_TRANS_TYPE_NONE No Masking will be used/required.
+ * PDF_TRANS_TYPE_BINARY Pixels are either fully opaque/fully transparent.
+ * PDF_TRANS_TYPE_ALPHA Uses alpha channel, requies SMask.(PDF-1.4)
+ *
+ * check_transparency() must check the current setting of output PDF version
+ * and must choose appropriate trans_type value according to PDF version of
+ * current output PDF document.
+ *
+ * If the PDF version is less than 1.3, no transparency is supported for this
+ * version of PDF, hence PDF_TRANS_TYPE_NONE must be returned. And when the PDF
+ * version is equal to 1.3, possible retrun values are PDF_TRANS_TYPE_BINARY or
+ * PDF_TRANS_TYPE_NONE. The latter case arises when PNG file uses alpha channel
+ * explicitly (color type PNG_COLOR_TYPE_XXX_ALPHA), or the tRNS chunk for the
+ * PNG_COLOR_TYPE_PALETTE image contains intermediate values of opacity.
+ *
+ * Finally, in the case of PDF version 1.4, all kind of translucent pixels can
+ * be represented with Soft-Mask.
+ */
+
+static int
+check_transparency (png_structp png_ptr, png_infop info_ptr)
+{
+ int trans_type;
+ unsigned pdf_version;
+ png_byte color_type;
+ png_color_16p trans_values;
+ png_bytep trans;
+ int num_trans;
+
+ pdf_version = pdf_get_version();
+ color_type = png_get_color_type(png_ptr, info_ptr);
+
+ /*
+ * First we set trans_type to appropriate value for PNG image.
+ */
+ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ trans_type = PDF_TRANS_TYPE_ALPHA;
+ } else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) &&
+ png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values)) {
+ /* Have valid tRNS chunk. */
+ switch (color_type) {
+ case PNG_COLOR_TYPE_PALETTE:
+ /* Use color-key mask if possible. */
+ trans_type = PDF_TRANS_TYPE_BINARY;
+ while (num_trans-- > 0) {
+ if (trans[num_trans] != 0x00 && trans[num_trans] != 0xff) {
+ /* This seems not binary transparency */
+ trans_type = PDF_TRANS_TYPE_ALPHA;
+ break;
+ }
+ }
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ case PNG_COLOR_TYPE_RGB:
+ /* RGB or GRAY, single color specified by trans_values is transparent. */
+ trans_type = PDF_TRANS_TYPE_BINARY;
+ break;
+ default:
+ /* Else tRNS silently ignored. */
+ trans_type = PDF_TRANS_TYPE_NONE;
+ }
+ } else { /* no transparency */
+ trans_type = PDF_TRANS_TYPE_NONE;
+ }
+
+ /*
+ * Now we check PDF version.
+ * We can convert alpha cahnnels to explicit mask via user supplied alpha-
+ * threshold value. But I will not do that.
+ */
+ if (( pdf_version < 3 && trans_type != PDF_TRANS_TYPE_NONE ) ||
+ ( pdf_version < 4 && trans_type == PDF_TRANS_TYPE_ALPHA )) {
+ /*
+ * No transparency supported but PNG uses transparency, or Soft-Mask
+ * required but no support for it is available in this version of PDF.
+ * We must do pre-composition of image with the background image here. But,
+ * we cannot do that in general since dvipdfmx is not a rasterizer. What we
+ * can do here is to composite image with a rectangle filled with the
+ * background color. However, images are stored as an Image XObject which
+ * can be referenced anywhere in the PDF document content. Hence, we cannot
+ * know the correct background color at this time. So we will choose white
+ * as background color, which is most probable color in our cases.
+ * We ignore bKGD chunk.
+ */
+ png_color_16 bg;
+ bg.red = 255; bg.green = 255; bg.blue = 255; bg.gray = 255; bg.index = 0;
+ png_set_background(png_ptr, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+ WARN("%s: Transparency will be ignored. (no support in PDF ver. < 1.3)", PNG_DEBUG_STR);
+ if (pdf_version < 3)
+ WARN("%s: Please use -V 3 option to enable binary transparency support.", PNG_DEBUG_STR);
+ if (pdf_version < 4)
+ WARN("%s: Please use -V 4 option to enable full alpha channel support.", PNG_DEBUG_STR);
+ trans_type = PDF_TRANS_TYPE_NONE;
+ }
+
+ return trans_type;
+}
+
+/*
+ * sRGB:
+ *
+ * If sRGB chunk is present, cHRM and gAMA chunk must be ignored.
+ *
+ */
+static pdf_obj *
+get_rendering_intent (png_structp png_ptr, png_infop info_ptr)
+{
+ pdf_obj *intent;
+ int srgb_intent;
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB) &&
+ png_get_sRGB (png_ptr, info_ptr, &srgb_intent)) {
+ switch (srgb_intent) {
+ case PNG_sRGB_INTENT_SATURATION:
+ intent = pdf_new_name("Saturation");
+ break;
+ case PNG_sRGB_INTENT_PERCEPTUAL:
+ intent = pdf_new_name("Perceptual");
+ break;
+ case PNG_sRGB_INTENT_ABSOLUTE:
+ intent = pdf_new_name("AbsoluteColorimetric");
+ break;
+ case PNG_sRGB_INTENT_RELATIVE:
+ intent = pdf_new_name("RelativeColorimetric");
+ break;
+ default:
+ WARN("%s: Invalid value in PNG sRGB chunk: %d", PNG_DEBUG_STR, srgb_intent);
+ intent = NULL;
+ }
+ } else
+ intent = NULL;
+
+ return intent;
+}
+
+/* Approximated sRGB */
+static pdf_obj *
+create_cspace_sRGB (png_structp png_ptr, png_infop info_ptr)
+{
+ pdf_obj *colorspace;
+ pdf_obj *cal_param;
+ png_byte color_type;
+
+ color_type = png_get_color_type(png_ptr, info_ptr);
+
+ /* Parameters taken from PNG spec. section 4.2.2.3. */
+ cal_param = make_param_Cal(color_type,
+ 2.2,
+ 0.3127, 0.329,
+ 0.64, 0.33, 0.3, 0.6, 0.15, 0.06);
+ if (!cal_param)
+ return NULL;
+
+ colorspace = pdf_new_array();
+
+ switch (color_type) {
+ case PNG_COLOR_TYPE_RGB:
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ case PNG_COLOR_TYPE_PALETTE:
+ pdf_add_array(colorspace, pdf_new_name("CalRGB"));
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ pdf_add_array(colorspace, pdf_new_name("CalGray"));
+ break;
+ }
+ pdf_add_array(colorspace, cal_param);
+
+ return colorspace;
+}
+
+static pdf_obj *
+create_cspace_ICCBased (png_structp png_ptr, png_infop info_ptr)
+{
+ pdf_obj *colorspace;
+ int csp_id, colortype;
+ png_byte color_type;
+ png_charp name;
+ int compression_type; /* Manual page for libpng does not
+ * clarify whether profile data is inflated by libpng.
+ */
+#if PNG_LIBPNG_VER_MINOR < 5
+ png_charp profile;
+#else
+ png_bytep profile;
+#endif
+ png_uint_32 proflen;
+
+ if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP) ||
+ !png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile, &proflen))
+ return NULL;
+
+ color_type = png_get_color_type(png_ptr, info_ptr);
+
+ if (color_type & PNG_COLOR_MASK_COLOR) {
+ colortype = PDF_COLORSPACE_TYPE_RGB;
+#if 0
+ alternate = create_cspace_CalRGB(png_ptr, info_ptr);
+#endif
+ } else {
+ colortype = PDF_COLORSPACE_TYPE_GRAY;
+#if 0
+ alternate = create_cspace_CalGray(png_ptr, info_ptr);
+#endif
+ }
+
+#if 0
+ if (alternate)
+ pdf_add_dict(dict, pdf_new_name("Alternate"), alternate);
+#endif
+
+ if (iccp_check_colorspace(colortype, profile, proflen) < 0)
+ colorspace = NULL;
+ else {
+ csp_id = iccp_load_profile(name, profile, proflen);
+ if (csp_id < 0) {
+ colorspace = NULL;
+ } else {
+ colorspace = pdf_get_colorspace_reference(csp_id);
+ }
+ }
+
+ /* Rendering intent ... */
+
+ return colorspace;
+}
+
+/*
+ * gAMA, cHRM:
+ *
+ * If cHRM is present, we use CIE-Based color space. gAMA is also used here
+ * if available.
+ */
+
+#define INVALID_CHRM_VALUE(xw,yw,xr,yr,xg,yg,xb,yb) (\
+ (xw) <= 0.0 || (yw) < 1.0e-10 || \
+ (xr) < 0.0 || (yr) < 0.0 || (xg) < 0.0 || (yg) < 0.0 || \
+ (xb) < 0.0 || (yb) < 0.0)
+
+static pdf_obj *
+create_cspace_CalRGB (png_structp png_ptr, png_infop info_ptr)
+{
+ pdf_obj *colorspace;
+ pdf_obj *cal_param;
+ double xw, yw, xr, yr, xg, yg, xb, yb;
+ double G;
+
+ if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM) ||
+ !png_get_cHRM(png_ptr, info_ptr, &xw, &yw, &xr, &yr, &xg, &yg, &xb, &yb))
+ return NULL;
+
+ if (xw <= 0.0 || yw < 1.0e-10 ||
+ xr < 0.0 || yr < 0.0 || xg < 0.0 || yg < 0.0 || xb < 0.0 || yb < 0.0) {
+ WARN("%s: Invalid cHRM chunk parameters found.", PNG_DEBUG_STR);
+ return NULL;
+ }
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
+ png_get_gAMA (png_ptr, info_ptr, &G)) {
+ if (G < 1.0e-2) {
+ WARN("%s: Unusual Gamma value: %g", PNG_DEBUG_STR, G);
+ return NULL;
+ }
+ G = 1.0 / G; /* Gamma is inverted. */
+ } else {
+ G = 1.0;
+ }
+
+ cal_param = make_param_Cal(PNG_COLOR_TYPE_RGB, G, xw, yw, xr, yr, xg, yg, xb, yb);
+
+ if (!cal_param)
+ return NULL;
+
+ colorspace = pdf_new_array();
+ pdf_add_array(colorspace, pdf_new_name("CalRGB"));
+ pdf_add_array(colorspace, cal_param);
+
+ return colorspace;
+}
+
+static pdf_obj *
+create_cspace_CalGray (png_structp png_ptr, png_infop info_ptr)
+{
+ pdf_obj *colorspace;
+ pdf_obj *cal_param;
+ double xw, yw, xr, yr, xg, yg, xb, yb;
+ double G;
+
+ if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM) ||
+ !png_get_cHRM(png_ptr, info_ptr, &xw, &yw, &xr, &yr, &xg, &yg, &xb, &yb))
+ return NULL;
+
+ if (xw <= 0.0 || yw < 1.0e-10 ||
+ xr < 0.0 || yr < 0.0 || xg < 0.0 || yg < 0.0 || xb < 0.0 || yb < 0.0) {
+ WARN("%s: Invalid cHRM chunk parameters found.", PNG_DEBUG_STR);
+ return NULL;
+ }
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
+ png_get_gAMA (png_ptr, info_ptr, &G)) {
+ if (G < 1.0e-2) {
+ WARN("%s: Unusual Gamma value: %g", PNG_DEBUG_STR, G);
+ return NULL;
+ }
+ G = 1.0 / G; /* Gamma is inverted. */
+ } else {
+ G = 1.0;
+ }
+
+ cal_param = make_param_Cal(PNG_COLOR_TYPE_GRAY, G, xw, yw, xr, yr, xg, yg, xb, yb);
+
+ if (!cal_param)
+ return NULL;
+
+ colorspace = pdf_new_array();
+ pdf_add_array(colorspace, pdf_new_name("CalGray"));
+ pdf_add_array(colorspace, cal_param);
+
+ return colorspace;
+}
+
+static pdf_obj *
+make_param_Cal (png_byte color_type,
+ double G, /* Gamma */
+ double xw, double yw,
+ double xr, double yr, double xg, double yg, double xb, double yb)
+{
+ pdf_obj *cal_param;
+ pdf_obj *white_point, *matrix, *dev_gamma;
+ double Xw, Yw, Zw; /* Yw = 1.0 */
+ double Xr, Xg, Xb, Yr, Yb, Yg, Zr, Zg, Zb;
+
+#ifndef ABS
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#endif
+ /*
+ * TODO: Check validity
+ *
+ * Conversion found in
+ *
+ * com.sixlegs.image.png - Java package to read and display PNG images
+ * Copyright (C) 1998, 1999, 2001 Chris Nokleberg
+ *
+ * http://www.sixlegs.com/software/png/
+ *
+ */
+ {
+ double zw, zr, zg, zb;
+ double fr, fg, fb;
+ double det;
+
+ /* WhitePoint */
+ zw = 1 - (xw + yw);
+ zr = 1 - (xr + yr); zg = 1 - (xg + yg); zb = 1 - (xb + yb);
+ Xw = xw / yw; Yw = 1.0; Zw = zw / yw;
+
+ /* Matrix */
+ det = xr * (yg * zb - zg * yb) - xg * (yr * zb - zr * yb) + xb * (yr * zg - zr * yg);
+ if (ABS(det) < 1.0e-10) {
+ WARN("Non invertible matrix: Maybe invalid value(s) specified in cHRM chunk.");
+ return NULL;
+ }
+ fr = (Xw * (yg * zb - zg * yb) - xg * (zb - Zw * yb) + xb * (zg - Zw * yg)) / det;
+ fg = (xr * (zb - Zw * yb) - Xw * (yr * zb - zr * yb) + xb * (yr * Zw - zr)) / det;
+ fb = (xr * (yg * Zw - zg) - xg * (yr * Zw - zr) + Xw * (yr * zg - zr * yg)) / det;
+ Xr = fr * xr; Yr = fr * yr; Zr = fr * zr;
+ Xg = fg * xg; Yg = fg * yg; Zg = fg * zg;
+ Xb = fb * xb; Yb = fb * yb; Zb = fb * zb;
+ }
+
+ if (G < 1.0e-2) {
+ WARN("Unusual Gamma specified: %g", G);
+ return NULL;
+ }
+
+ cal_param = pdf_new_dict();
+
+ /* White point is always required. */
+ white_point = pdf_new_array();
+ pdf_add_array(white_point, pdf_new_number(ROUND(Xw, 0.00001)));
+ pdf_add_array(white_point, pdf_new_number(ROUND(Yw, 0.00001)));
+ pdf_add_array(white_point, pdf_new_number(ROUND(Zw, 0.00001)));
+ pdf_add_dict(cal_param, pdf_new_name("WhitePoint"), white_point);
+
+ /* Matrix - default: Identity */
+ if (color_type & PNG_COLOR_MASK_COLOR) {
+ if (G != 1.0) {
+ dev_gamma = pdf_new_array();
+ pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
+ pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
+ pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
+ pdf_add_dict(cal_param, pdf_new_name("Gamma"), dev_gamma);
+ }
+
+ matrix = pdf_new_array();
+ pdf_add_array(matrix, pdf_new_number(ROUND(Xr, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Yr, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Zr, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Xg, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Yg, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Zg, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Xb, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Yb, 0.00001)));
+ pdf_add_array(matrix, pdf_new_number(ROUND(Zb, 0.00001)));
+ pdf_add_dict (cal_param, pdf_new_name("Matrix"), matrix);
+ } else { /* Gray */
+ if (G != 1.0)
+ pdf_add_dict(cal_param,
+ pdf_new_name("Gamma"),
+ pdf_new_number(ROUND(G, 0.00001)));
+ }
+
+ return cal_param;
+}
+
+/*
+ * Set up Indexed ColorSpace for color-type PALETTE:
+ *
+ * PNG allows only RGB color for base color space. If gAMA and/or cHRM
+ * chunk is available, we can use CalRGB color space instead of DeviceRGB
+ * for base color space.
+ *
+ */
+static pdf_obj *
+create_cspace_Indexed (png_structp png_ptr, png_infop info_ptr)
+{
+ pdf_obj *colorspace;
+ pdf_obj *base, *lookup;
+ png_byte *data_ptr;
+ png_colorp plte;
+ int num_plte, i;
+
+ if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE) ||
+ !png_get_PLTE(png_ptr, info_ptr, &plte, &num_plte)) {
+ WARN("%s: PNG does not have valid PLTE chunk.", PNG_DEBUG_STR);
+ return NULL;
+ }
+
+ /* Order is important. */
+ colorspace = pdf_new_array ();
+ pdf_add_array(colorspace, pdf_new_name("Indexed"));
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP))
+ base = create_cspace_ICCBased(png_ptr, info_ptr);
+ else {
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB))
+ base = create_cspace_sRGB(png_ptr, info_ptr);
+ else
+ base = create_cspace_CalRGB(png_ptr, info_ptr);
+ }
+
+ if (!base)
+ base = pdf_new_name("DeviceRGB");
+
+ pdf_add_array(colorspace, base);
+ pdf_add_array(colorspace, pdf_new_number(num_plte-1));
+ data_ptr = NEW(num_plte*3, png_byte);
+ for (i = 0; i < num_plte; i++) {
+ data_ptr[3*i] = plte[i].red;
+ data_ptr[3*i+1] = plte[i].green;
+ data_ptr[3*i+2] = plte[i].blue;
+ }
+ lookup = pdf_new_string(data_ptr, num_plte*3);
+ RELEASE(data_ptr);
+ pdf_add_array(colorspace, lookup);
+
+ return colorspace;
+}
+
+/*
+ * pHYs: no support
+ *
+ * pngimage.c is not responsible for adjusting image size.
+ * Higher layer must do something for this.
+ */
+
+/*
+ * Colorkey Mask: array
+ *
+ * [component_0_min component_0_max ... component_n_min component_n_max]
+ *
+ */
+
+static pdf_obj *
+create_ckey_mask (png_structp png_ptr, png_infop info_ptr)
+{
+ pdf_obj *colorkeys;
+ png_byte color_type;
+ png_bytep trans;
+ int num_trans, i;
+ png_color_16p colors;
+
+ if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
+ !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &colors)) {
+ WARN("%s: PNG does not have valid tRNS chunk!", PNG_DEBUG_STR);
+ return NULL;
+ }
+
+ colorkeys = pdf_new_array();
+ color_type = png_get_color_type(png_ptr, info_ptr);
+
+ switch (color_type) {
+ case PNG_COLOR_TYPE_PALETTE:
+ for (i = 0; i < num_trans; i++) {
+ if (trans[i] == 0x00) {
+ pdf_add_array(colorkeys, pdf_new_number(i));
+ pdf_add_array(colorkeys, pdf_new_number(i));
+ } else if (trans[i] != 0xff) {
+ WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
+ }
+ }
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ pdf_add_array(colorkeys, pdf_new_number(colors->red));
+ pdf_add_array(colorkeys, pdf_new_number(colors->red));
+ pdf_add_array(colorkeys, pdf_new_number(colors->green));
+ pdf_add_array(colorkeys, pdf_new_number(colors->green));
+ pdf_add_array(colorkeys, pdf_new_number(colors->blue));
+ pdf_add_array(colorkeys, pdf_new_number(colors->blue));
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ pdf_add_array(colorkeys, pdf_new_number(colors->gray));
+ pdf_add_array(colorkeys, pdf_new_number(colors->gray));
+ break;
+ default:
+ WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
+ pdf_release_obj(colorkeys);
+ colorkeys = NULL;
+ }
+
+ return colorkeys;
+}
+
+/*
+ * Soft-Mask: stream
+ *
+ * <<
+ * /Type /XObject
+ * /Subtype /Image
+ * /Width -int-
+ * /Height -int-
+ * /BitsPerComponent bpc
+ * >>
+ * stream .... endstream
+ *
+ * ColorSpace, Mask, SMask must be absent. ImageMask must be false or absent.
+ */
+
+static pdf_obj *
+create_soft_mask (png_structp png_ptr, png_infop info_ptr,
+ png_bytep image_data_ptr, png_uint_32 width, png_uint_32 height)
+{
+ pdf_obj *smask, *dict;
+ png_bytep smask_data_ptr;
+ png_bytep trans;
+ int num_trans;
+ png_uint_32 i;
+
+ if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
+ !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL)) {
+ WARN("%s: PNG does not have valid tRNS chunk but tRNS is requested.", PNG_DEBUG_STR);
+ return NULL;
+ }
+
+ smask = pdf_new_stream(STREAM_COMPRESS);
+ dict = pdf_stream_dict(smask);
+ smask_data_ptr = (png_bytep) NEW(width*height, png_byte);
+ pdf_add_dict(dict, pdf_new_name("Type"), pdf_new_name("XObjcect"));
+ pdf_add_dict(dict, pdf_new_name("Subtype"), pdf_new_name("Image"));
+ pdf_add_dict(dict, pdf_new_name("Width"), pdf_new_number(width));
+ pdf_add_dict(dict, pdf_new_name("Height"), pdf_new_number(height));
+ pdf_add_dict(dict, pdf_new_name("ColorSpace"), pdf_new_name("DeviceGray"));
+ pdf_add_dict(dict, pdf_new_name("BitsPerComponent"), pdf_new_number(8));
+ for (i = 0; i < width*height; i++) {
+ png_byte idx = image_data_ptr[i];
+ smask_data_ptr[i] = (idx < num_trans) ? trans[idx] : 0xff;
+ }
+ pdf_add_stream(smask, (char *)smask_data_ptr, width*height);
+ RELEASE(smask_data_ptr);
+
+ return smask;
+}
+
+/* bitdepth is always 8 (16 is not supported) */
+static pdf_obj *
+strip_soft_mask (png_structp png_ptr, png_infop info_ptr,
+ /* next two values will be modified. */
+ png_bytep image_data_ptr, png_uint_32p rowbytes_ptr,
+ png_uint_32 width, png_uint_32 height)
+{
+ pdf_obj *smask, *dict;
+ png_byte color_type;
+ png_bytep smask_data_ptr;
+ png_uint_32 i;
+
+ color_type = png_get_color_type(png_ptr, info_ptr);
+
+ if (color_type & PNG_COLOR_MASK_COLOR) {
+ if (*rowbytes_ptr != 4*width*sizeof(png_byte)) { /* Something wrong */
+ WARN("%s: Inconsistent rowbytes value.", PNG_DEBUG_STR);
+ return NULL;
+ }
+ } else {
+ if (*rowbytes_ptr != 2*width*sizeof(png_byte)) { /* Something wrong */
+ WARN("%s: Inconsistent rowbytes value.", PNG_DEBUG_STR);
+ return NULL;
+ }
+ }
+
+ smask = pdf_new_stream(STREAM_COMPRESS);
+ dict = pdf_stream_dict(smask);
+ pdf_add_dict(dict, pdf_new_name("Type"), pdf_new_name("XObjcect"));
+ pdf_add_dict(dict, pdf_new_name("Subtype"), pdf_new_name("Image"));
+ pdf_add_dict(dict, pdf_new_name("Width"), pdf_new_number(width));
+ pdf_add_dict(dict, pdf_new_name("Height"), pdf_new_number(height));
+ pdf_add_dict(dict, pdf_new_name("ColorSpace"), pdf_new_name("DeviceGray"));
+ pdf_add_dict(dict, pdf_new_name("BitsPerComponent"), pdf_new_number(8));
+
+ smask_data_ptr = (png_bytep) NEW(width*height, png_byte);
+
+ switch (color_type) {
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ for (i = 0; i < width*height; i++) {
+ memmove(image_data_ptr+(3*i), image_data_ptr+(4*i), 3);
+ smask_data_ptr[i] = image_data_ptr[4*i+3];
+ }
+ *rowbytes_ptr = 3*width*sizeof(png_byte);
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ for (i = 0; i < width*height; i++) {
+ image_data_ptr[i] = image_data_ptr[2*i];
+ smask_data_ptr[i] = image_data_ptr[2*i+1];
+ }
+ *rowbytes_ptr = width*sizeof(png_byte);
+ break;
+ default:
+ WARN("You found a bug in pngimage.c!");
+ pdf_release_obj(smask);
+ RELEASE(smask_data_ptr);
+ return NULL;
+ }
+
+ pdf_add_stream(smask, smask_data_ptr, width*height);
+ RELEASE(smask_data_ptr);
+
+ return smask;
+}
+
+static void
+read_image_data (png_structp png_ptr, png_infop info_ptr, /* info_ptr unused */
+ png_bytep dest_ptr, png_uint_32 height, png_uint_32 rowbytes)
+{
+ png_bytepp rows_p;
+ png_uint_32 i;
+
+ rows_p = (png_bytepp) NEW (height, png_bytep);
+ for (i=0; i< height; i++)
+ rows_p[i] = dest_ptr + (rowbytes * i);
+ png_read_image(png_ptr, rows_p);
+ RELEASE(rows_p);
+}
+
+int
+png_get_bbox (FILE *png_file, long *width, long *height,
+ double *xdensity, double *ydensity)
+{
+ png_structp png_ptr;
+ png_infop png_info_ptr;
+ png_uint_32 xppm, yppm;
+
+ rewind (png_file);
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png_ptr == NULL ||
+ (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) {
+ WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR);
+ if (png_ptr)
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return -1;
+ }
+
+ /* Inititializing file IO. */
+ png_init_io (png_ptr, png_file);
+
+ /* Read PNG info-header and get some info. */
+ png_read_info(png_ptr, png_info_ptr);
+ *width = png_get_image_width (png_ptr, png_info_ptr);
+ *height = png_get_image_height(png_ptr, png_info_ptr);
+ xppm = png_get_x_pixels_per_meter(png_ptr, png_info_ptr);
+ yppm = png_get_y_pixels_per_meter(png_ptr, png_info_ptr);
+
+ /* Cleanup */
+ if (png_info_ptr)
+ png_destroy_info_struct(png_ptr, &png_info_ptr);
+ if (png_ptr)
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+
+ if (compat_mode)
+ *xdensity = *ydensity = 72.0 / 100.0;
+ else {
+ *xdensity = xppm ? 72.0 / 0.0254 / xppm : 1.0;
+ *ydensity = yppm ? 72.0 / 0.0254 / yppm : 1.0;
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_LIBPNG */