Back to index

texmacs  1.0.7.15
pngimage.c
Go to the documentation of this file.
00001 /*  $Header: /home/cvsroot/dvipdfmx/src/pngimage.c,v 1.28 2010/02/23 03:28:10 matthias Exp $
00002 
00003     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
00004 
00005     Copyright (C) 2007 by Jin-Hwan Cho and Shunsaku Hirata,
00006     the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
00007     
00008     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014     
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019     
00020     You should have received a copy of the GNU General Public License
00021     along with this program; if not, write to the Free Software
00022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00023 */
00024 
00025 #if HAVE_CONFIG_H
00026 #include "config.h"
00027 #endif
00028 
00029 /*
00030  * PNG SUPPORT
00031  *
00032  *  All bitdepth less than 16 is supported.
00033  *  Supported color types are: PALETTE, RGB, GRAY, RGB_ALPHA, GRAY_ALPHA.
00034  *  Supported ancillary chunks: tRNS, cHRM + gAMA, (sRGB), (iCCP)
00035  * 
00036  *  gAMA support is available only when cHRM exists. cHRM support is not
00037  *  tested well. CalRGB/CalGray colorspace is used for PNG images that
00038  *  have cHRM chunk (but not sRGB).
00039  *
00040  * LIMITATIONS
00041  *
00042  *   Recent version of PDF (>= 1.5) support 16 bpc, but 16 bit bitdepth PNG
00043  *   images are automatically converted to 8 bit bitpedth image.
00044  *
00045  * TODO
00046  *
00047  *  sBIT ? iTXT, tEXT and tIME as MetaData ?, pHYS (see below)
00048  *  16 bpc support for PDF-1.5. JBIG compression for monochrome image.
00049  *  Predictor for deflate ?
00050  */
00051 
00052 #include "system.h"
00053 #include "error.h"
00054 #include "mem.h"
00055 
00056 #include "dvipdfmx.h"
00057 
00058 #include "pdfcolor.h"
00059 #include "pdfobj.h"
00060 
00061 #define PNG_DEBUG_STR "PNG"
00062 #define PNG_DEBUG     3
00063 
00064 #ifdef HAVE_LIBPNG
00065 
00066 /*
00067  * Write, MNG, Progressive not required.
00068  */
00069 #define PNG_NO_WRITE_SUPPORTED
00070 #define PNG_NO_MNG_FEATURES
00071 #define PNG_NO_PROGRESSIVE_READ
00072 #if 0
00073 /* 16_TO_8 required. */
00074 #define PNG_NO_READ_TRANSFORMS
00075 #endif
00076 
00077 #include <png.h>
00078 #include "pngimage.h"
00079 
00080 #include "pdfximage.h"
00081 
00082 #define PDF_TRANS_TYPE_NONE   0
00083 #define PDF_TRANS_TYPE_BINARY 1
00084 #define PDF_TRANS_TYPE_ALPHA  2
00085 
00086 /* ColorSpace */
00087 static pdf_obj *create_cspace_Indexed  (png_structp png_ptr, png_infop info_ptr);
00088 
00089 /* CIE-Based: CalRGB/CalGray
00090  *
00091  * We ignore gAMA if cHRM is not found.
00092  */
00093 static pdf_obj *create_cspace_CalRGB   (png_structp png_ptr, png_infop info_ptr);
00094 static pdf_obj *create_cspace_CalGray  (png_structp png_ptr, png_infop info_ptr);
00095 static pdf_obj *make_param_Cal         (png_byte color_type,
00096                                    double G,
00097                                    double xw, double yw,
00098                                    double xr, double yr,
00099                                    double xg, double yg,
00100                                    double xb, double yb);
00101 
00102 /* sRGB:
00103  *
00104  * We (and PDF) do not have direct sRGB support. The sRGB color space can be
00105  * precisely represented by ICC profile, but we use approximate CalRGB color
00106  * space.
00107  */
00108 static pdf_obj *create_cspace_sRGB    (png_structp png_ptr, png_infop info_ptr);
00109 static pdf_obj *get_rendering_intent  (png_structp png_ptr, png_infop info_ptr);
00110 
00111 /* ICCBased:
00112  *
00113  * Not supported yet.
00114  * Must check if ICC profile is valid and can be imported to PDF.
00115  * There are few restrictions (should be applied to PNG too?) in ICC profile
00116  * support in PDF. Some information should be obtained from profile.
00117  */
00118 static pdf_obj *create_cspace_ICCBased (png_structp png_ptr, png_infop info_ptr);
00119 
00120 /* Transparency */
00121 static int      check_transparency (png_structp png_ptr, png_infop info_ptr);
00122 /* Color-Key Mask */
00123 static pdf_obj *create_ckey_mask   (png_structp png_ptr, png_infop info_ptr);
00124 /* Soft Mask:
00125  *
00126  * create_soft_mask() is for PNG_COLOR_TYPE_PALLETE.
00127  * Images with alpha chunnel use strip_soft_mask().
00128  * An object representing mask itself is returned.
00129  */
00130 static pdf_obj *create_soft_mask   (png_structp png_ptr, png_infop info_ptr,
00131                                 png_bytep image_data_ptr,
00132                                 png_uint_32 width, png_uint_32 height);
00133 static pdf_obj *strip_soft_mask    (png_structp png_ptr, png_infop info_ptr,
00134                                 png_bytep image_data_ptr,
00135                                 png_uint_32p rowbytes_ptr,
00136                                 png_uint_32 width, png_uint_32 height);
00137 
00138 /* Read image body */
00139 static void read_image_data (png_structp png_ptr, png_infop info_ptr,
00140                           png_bytep dest_ptr,
00141                           png_uint_32 height, png_uint_32 rowbytes);
00142 
00143 int
00144 check_for_png (FILE *png_file) 
00145 {
00146   unsigned char sigbytes[4];
00147 
00148   rewind (png_file);
00149   if (fread (sigbytes, 1, sizeof(sigbytes), png_file) !=
00150       sizeof(sigbytes) ||
00151       (png_sig_cmp (sigbytes, 0, sizeof(sigbytes))))
00152     return 0;
00153   else
00154     return 1;
00155 }
00156 
00157 int
00158 png_include_image (pdf_ximage *ximage, FILE *png_file)
00159 {
00160   pdf_obj  *stream;
00161   pdf_obj  *stream_dict;
00162   pdf_obj  *colorspace, *mask, *intent;
00163   png_bytep stream_data_ptr;
00164   int       trans_type;
00165   ximage_info info;
00166   /* Libpng stuff */
00167   png_structp png_ptr;
00168   png_infop   png_info_ptr;
00169   png_byte    bpc, color_type;
00170   png_uint_32 width, height, rowbytes, xppm, yppm;
00171 
00172   pdf_ximage_init_image_info(&info);
00173 
00174   stream      = NULL;
00175   stream_dict = NULL;
00176   colorspace  = mask = intent = NULL;
00177 
00178   rewind (png_file);
00179   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00180   if (png_ptr == NULL || 
00181       (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) {
00182     WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR);
00183     if (png_ptr)
00184       png_destroy_read_struct(&png_ptr, NULL, NULL);
00185     return -1;
00186   }
00187 
00188   /* Inititializing file IO. */
00189   png_init_io (png_ptr, png_file);
00190 
00191   /* Read PNG info-header and get some info. */
00192   png_read_info(png_ptr, png_info_ptr);
00193   color_type = png_get_color_type  (png_ptr, png_info_ptr);
00194   width      = png_get_image_width (png_ptr, png_info_ptr);
00195   height     = png_get_image_height(png_ptr, png_info_ptr);
00196   bpc        = png_get_bit_depth   (png_ptr, png_info_ptr);
00197   xppm       = png_get_x_pixels_per_meter(png_ptr, png_info_ptr);
00198   yppm       = png_get_y_pixels_per_meter(png_ptr, png_info_ptr);
00199 
00200   /* We do not need 16-bpc color. Ask libpng to convert down to 8-bpc. */
00201   if (bpc > 8) {
00202     png_set_strip_16(png_ptr);
00203     bpc = 8;
00204   }
00205 
00206   trans_type = check_transparency(png_ptr, png_info_ptr);
00207   /* check_transparency() does not do updata_info() */
00208   png_read_update_info(png_ptr, png_info_ptr);
00209   rowbytes = png_get_rowbytes(png_ptr, png_info_ptr);
00210 
00211   /* Values listed below will not be modified in the remaining process. */
00212   info.width  = width;
00213   info.height = height;
00214   info.bits_per_component = bpc;
00215 
00216   if (compat_mode)
00217     info.xdensity = info.ydensity = 72.0 / 100.0;
00218   else {
00219     if (xppm > 0)
00220       info.xdensity = 72.0 / 0.0254 / xppm;
00221     if (yppm > 0)
00222       info.ydensity = 72.0 / 0.0254 / yppm;
00223   }
00224 
00225   stream      = pdf_new_stream (STREAM_COMPRESS);
00226   stream_dict = pdf_stream_dict(stream);
00227 
00228   stream_data_ptr = (png_bytep) NEW(rowbytes*height, png_byte);
00229   read_image_data(png_ptr, png_info_ptr, stream_data_ptr, height, rowbytes);
00230 
00231   /* Non-NULL intent means there is valid sRGB chunk. */
00232   intent = get_rendering_intent(png_ptr, png_info_ptr);
00233   if (intent)
00234     pdf_add_dict(stream_dict, pdf_new_name("Intent"), intent);
00235 
00236   switch (color_type) {
00237   case PNG_COLOR_TYPE_PALETTE:
00238 
00239     colorspace = create_cspace_Indexed(png_ptr, png_info_ptr);
00240 
00241     switch (trans_type) {
00242     case PDF_TRANS_TYPE_BINARY:
00243       /* Color-key masking */
00244       mask = create_ckey_mask(png_ptr, png_info_ptr);
00245       break;
00246     case PDF_TRANS_TYPE_ALPHA:
00247       /* Soft mask */
00248       mask = create_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, width, height);
00249       break;
00250     default:
00251       /* Nothing to be done here.
00252        * No tRNS chunk or image already composited with background color.
00253        */
00254       break;
00255     }
00256     break;
00257   case PNG_COLOR_TYPE_RGB:
00258   case PNG_COLOR_TYPE_RGB_ALPHA:
00259 
00260     if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP))
00261       colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr);
00262     else if (intent) {
00263       colorspace = create_cspace_sRGB(png_ptr, png_info_ptr);
00264     } else {
00265       colorspace = create_cspace_CalRGB(png_ptr, png_info_ptr);
00266     }
00267     if (!colorspace)
00268       colorspace = pdf_new_name("DeviceRGB");
00269 
00270     switch (trans_type) {
00271     case PDF_TRANS_TYPE_BINARY:
00272       if (color_type != PNG_COLOR_TYPE_RGB)
00273        ERROR("Unexpected error in png_include_image().");
00274       mask = create_ckey_mask(png_ptr, png_info_ptr);
00275       break;
00276     /* rowbytes changes 4 to 3 at here */
00277     case PDF_TRANS_TYPE_ALPHA:
00278       if (color_type != PNG_COLOR_TYPE_RGB_ALPHA)
00279        ERROR("Unexpected error in png_include_image().");
00280       mask = strip_soft_mask(png_ptr, png_info_ptr,
00281                           stream_data_ptr, &rowbytes, width, height);
00282       break;
00283     default:
00284       mask = NULL;
00285     }
00286     info.num_components = 3;
00287     break;
00288 
00289   case PNG_COLOR_TYPE_GRAY:
00290   case PNG_COLOR_TYPE_GRAY_ALPHA:
00291 
00292     if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP))
00293       colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr);
00294     else if (intent) {
00295       colorspace = create_cspace_sRGB(png_ptr, png_info_ptr);
00296     } else {
00297       colorspace = create_cspace_CalGray(png_ptr, png_info_ptr);
00298     }
00299     if (!colorspace)
00300       colorspace = pdf_new_name("DeviceGray");
00301 
00302     switch (trans_type) {
00303     case PDF_TRANS_TYPE_BINARY:
00304       if (color_type != PNG_COLOR_TYPE_GRAY)
00305        ERROR("Unexpected error in png_include_image().");
00306       mask = create_ckey_mask(png_ptr, png_info_ptr);
00307       break;
00308     case PDF_TRANS_TYPE_ALPHA:
00309       if (color_type != PNG_COLOR_TYPE_GRAY_ALPHA)
00310        ERROR("Unexpected error in png_include_image().");
00311       mask = strip_soft_mask(png_ptr, png_info_ptr,
00312                           stream_data_ptr, &rowbytes, width, height);
00313       break;
00314     default:
00315       mask = NULL;
00316     }
00317     info.num_components = 1;
00318     break;
00319 
00320   default:
00321     WARN("%s: Unknown PNG colortype %d.", PNG_DEBUG_STR, color_type);
00322   }
00323   pdf_add_dict(stream_dict, pdf_new_name("ColorSpace"), colorspace);
00324 
00325   pdf_add_stream(stream, stream_data_ptr, rowbytes*height);
00326   RELEASE(stream_data_ptr);
00327 
00328   if (mask) {
00329     if (trans_type == PDF_TRANS_TYPE_BINARY)
00330       pdf_add_dict(stream_dict, pdf_new_name("Mask"), mask);
00331     else if (trans_type == PDF_TRANS_TYPE_ALPHA) {
00332       pdf_add_dict(stream_dict, pdf_new_name("SMask"), pdf_ref_obj(mask));
00333       pdf_release_obj(mask);
00334     } else {
00335       WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
00336       pdf_release_obj(mask);
00337     }
00338   }
00339 
00340   png_read_end(png_ptr, NULL);
00341 
00342   /* Cleanup */
00343   if (png_info_ptr)
00344     png_destroy_info_struct(png_ptr, &png_info_ptr);
00345   if (png_ptr)
00346     png_destroy_read_struct(&png_ptr, NULL, NULL);
00347 
00348   pdf_ximage_set_image(ximage, &info, stream);
00349 
00350   return 0;
00351 }
00352 
00353 /* 
00354  * The returned value trans_type is the type of transparency to be used for
00355  * this image. Possible values are:
00356  *
00357  *   PDF_TRANS_TYPE_NONE    No Masking will be used/required.
00358  *   PDF_TRANS_TYPE_BINARY  Pixels are either fully opaque/fully transparent.
00359  *   PDF_TRANS_TYPE_ALPHA   Uses alpha channel, requies SMask.(PDF-1.4)
00360  *
00361  * check_transparency() must check the current setting of output PDF version
00362  * and must choose appropriate trans_type value according to PDF version of
00363  * current output PDF document.
00364  *
00365  * If the PDF version is less than 1.3, no transparency is supported for this
00366  * version of PDF, hence PDF_TRANS_TYPE_NONE must be returned. And when the PDF
00367  * version is equal to 1.3, possible retrun values are PDF_TRANS_TYPE_BINARY or
00368  * PDF_TRANS_TYPE_NONE. The latter case arises when PNG file uses alpha channel
00369  * explicitly (color type PNG_COLOR_TYPE_XXX_ALPHA), or the tRNS chunk for the
00370  * PNG_COLOR_TYPE_PALETTE image contains intermediate values of opacity.
00371  *
00372  * Finally, in the case of PDF version 1.4, all kind of translucent pixels can
00373  * be represented with Soft-Mask.
00374  */
00375 
00376 static int
00377 check_transparency (png_structp png_ptr, png_infop info_ptr)
00378 {
00379   int           trans_type;
00380   unsigned      pdf_version;
00381   png_byte      color_type;
00382   png_color_16p trans_values;
00383   png_bytep     trans;
00384   int           num_trans;
00385 
00386   pdf_version = pdf_get_version();
00387   color_type  = png_get_color_type(png_ptr, info_ptr);
00388 
00389   /*
00390    * First we set trans_type to appropriate value for PNG image.
00391    */
00392   if (color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
00393       color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
00394     trans_type = PDF_TRANS_TYPE_ALPHA;
00395   } else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) &&
00396             png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values)) {
00397     /* Have valid tRNS chunk. */
00398     switch (color_type) {
00399     case PNG_COLOR_TYPE_PALETTE:
00400       /* Use color-key mask if possible. */ 
00401       trans_type = PDF_TRANS_TYPE_BINARY;
00402       while (num_trans-- > 0) {
00403        if (trans[num_trans] != 0x00 && trans[num_trans] != 0xff) {
00404          /* This seems not binary transparency */
00405          trans_type = PDF_TRANS_TYPE_ALPHA;
00406          break;
00407        }
00408       }
00409       break;
00410     case PNG_COLOR_TYPE_GRAY:
00411     case PNG_COLOR_TYPE_RGB:
00412       /* RGB or GRAY, single color specified by trans_values is transparent. */
00413       trans_type = PDF_TRANS_TYPE_BINARY;
00414       break;
00415     default:
00416       /* Else tRNS silently ignored. */
00417       trans_type = PDF_TRANS_TYPE_NONE;
00418     }
00419   } else { /* no transparency */
00420     trans_type = PDF_TRANS_TYPE_NONE;
00421   }
00422 
00423   /*
00424    * Now we check PDF version.
00425    * We can convert alpha cahnnels to explicit mask via user supplied alpha-
00426    * threshold value. But I will not do that.
00427    */
00428   if (( pdf_version < 3 && trans_type != PDF_TRANS_TYPE_NONE   ) ||
00429       ( pdf_version < 4 && trans_type == PDF_TRANS_TYPE_ALPHA )) {
00430     /*
00431      *   No transparency supported but PNG uses transparency, or Soft-Mask
00432      * required but no support for it is available in this version of PDF.
00433      * We must do pre-composition of image with the background image here. But,
00434      * we cannot do that in general since dvipdfmx is not a rasterizer. What we
00435      * can do here is to composite image with a rectangle filled with the
00436      * background color. However, images are stored as an Image XObject which
00437      * can be referenced anywhere in the PDF document content. Hence, we cannot
00438      * know the correct background color at this time. So we will choose white
00439      * as background color, which is most probable color in our cases.
00440      * We ignore bKGD chunk.
00441      */
00442     png_color_16 bg;
00443     bg.red = 255; bg.green = 255; bg.blue  = 255; bg.gray = 255; bg.index = 0;
00444     png_set_background(png_ptr, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
00445     WARN("%s: Transparency will be ignored. (no support in PDF ver. < 1.3)", PNG_DEBUG_STR);
00446     if (pdf_version < 3)
00447       WARN("%s: Please use -V 3 option to enable binary transparency support.", PNG_DEBUG_STR);
00448     if (pdf_version < 4)
00449       WARN("%s: Please use -V 4 option to enable full alpha channel support.", PNG_DEBUG_STR);
00450     trans_type = PDF_TRANS_TYPE_NONE;
00451   }
00452 
00453   return trans_type;
00454 }
00455 
00456 /*
00457  * sRGB:
00458  *
00459  *   If sRGB chunk is present, cHRM and gAMA chunk must be ignored.
00460  *
00461  */
00462 static pdf_obj *
00463 get_rendering_intent (png_structp png_ptr, png_infop info_ptr)
00464 {
00465   pdf_obj *intent;
00466   int      srgb_intent;
00467 
00468   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB) &&
00469       png_get_sRGB (png_ptr, info_ptr, &srgb_intent)) {
00470     switch (srgb_intent) {
00471     case PNG_sRGB_INTENT_SATURATION:
00472       intent = pdf_new_name("Saturation");
00473       break;
00474     case PNG_sRGB_INTENT_PERCEPTUAL:
00475       intent = pdf_new_name("Perceptual");
00476       break;
00477     case PNG_sRGB_INTENT_ABSOLUTE:
00478       intent = pdf_new_name("AbsoluteColorimetric");
00479       break;
00480     case PNG_sRGB_INTENT_RELATIVE:
00481       intent = pdf_new_name("RelativeColorimetric");
00482       break;
00483     default:
00484       WARN("%s: Invalid value in PNG sRGB chunk: %d", PNG_DEBUG_STR, srgb_intent);
00485       intent = NULL;
00486     }
00487   } else
00488     intent = NULL;
00489 
00490   return intent;
00491 }
00492 
00493 /* Approximated sRGB */
00494 static pdf_obj *
00495 create_cspace_sRGB (png_structp png_ptr, png_infop info_ptr)
00496 {
00497   pdf_obj  *colorspace;
00498   pdf_obj  *cal_param;
00499   png_byte  color_type;
00500 
00501   color_type = png_get_color_type(png_ptr, info_ptr);
00502 
00503   /* Parameters taken from PNG spec. section 4.2.2.3. */
00504   cal_param = make_param_Cal(color_type,
00505                           2.2,
00506                           0.3127, 0.329,
00507                           0.64, 0.33, 0.3, 0.6, 0.15, 0.06);
00508   if (!cal_param)
00509     return NULL;
00510 
00511   colorspace = pdf_new_array();
00512 
00513   switch (color_type) {
00514   case PNG_COLOR_TYPE_RGB:
00515   case PNG_COLOR_TYPE_RGB_ALPHA:
00516   case PNG_COLOR_TYPE_PALETTE:
00517     pdf_add_array(colorspace, pdf_new_name("CalRGB"));
00518     break;
00519   case PNG_COLOR_TYPE_GRAY:
00520   case PNG_COLOR_TYPE_GRAY_ALPHA:
00521     pdf_add_array(colorspace, pdf_new_name("CalGray"));
00522     break;
00523   }
00524   pdf_add_array(colorspace, cal_param);
00525 
00526   return colorspace;
00527 }
00528 
00529 static pdf_obj *
00530 create_cspace_ICCBased (png_structp png_ptr, png_infop info_ptr)
00531 {
00532   pdf_obj   *colorspace;
00533   int        csp_id, colortype;
00534   png_byte   color_type;
00535   png_charp  name;
00536   int        compression_type;  /* Manual page for libpng does not
00537                              * clarify whether profile data is inflated by libpng.
00538                              */
00539   png_charp   profile;          /* Might be switched to png_bytep */
00540   png_uint_32 proflen;
00541 
00542   if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP) ||
00543       !png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile, &proflen))
00544     return NULL;
00545 
00546   color_type = png_get_color_type(png_ptr, info_ptr);
00547 
00548   if (color_type & PNG_COLOR_MASK_COLOR) {
00549     colortype = PDF_COLORSPACE_TYPE_RGB;
00550 #if 0
00551     alternate = create_cspace_CalRGB(png_ptr, info_ptr);
00552 #endif
00553   } else {
00554     colortype = PDF_COLORSPACE_TYPE_GRAY;
00555 #if 0
00556     alternate = create_cspace_CalGray(png_ptr, info_ptr);
00557 #endif
00558   }
00559 
00560 #if 0
00561   if (alternate)
00562     pdf_add_dict(dict, pdf_new_name("Alternate"), alternate);
00563 #endif
00564 
00565   if (iccp_check_colorspace(colortype, profile, proflen) < 0)
00566     colorspace = NULL;
00567   else {
00568     csp_id = iccp_load_profile(name, profile, proflen);
00569     if (csp_id < 0) {
00570       colorspace = NULL;
00571     } else {
00572       colorspace = pdf_get_colorspace_reference(csp_id);
00573     }
00574   }
00575 
00576   /* Rendering intent ... */
00577 
00578   return colorspace;
00579 }
00580 
00581 /*
00582  * gAMA, cHRM:
00583  *
00584  *   If cHRM is present, we use CIE-Based color space. gAMA is also used here
00585  * if available.
00586  */
00587 
00588 #define INVALID_CHRM_VALUE(xw,yw,xr,yr,xg,yg,xb,yb) (\
00589   (xw) <= 0.0 || (yw) < 1.0e-10 || \
00590   (xr) < 0.0  || (yr) < 0.0 || (xg) < 0.0 || (yg) < 0.0 || \
00591   (xb) < 0.0  || (yb) < 0.0)
00592 
00593 static pdf_obj *
00594 create_cspace_CalRGB (png_structp png_ptr, png_infop info_ptr)
00595 {
00596   pdf_obj *colorspace;
00597   pdf_obj *cal_param;
00598   double   xw, yw, xr, yr, xg, yg, xb, yb;
00599   double   G;
00600 
00601   if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM) ||
00602       !png_get_cHRM(png_ptr, info_ptr, &xw, &yw, &xr, &yr, &xg, &yg, &xb, &yb))
00603     return NULL;
00604 
00605   if (xw <= 0.0 || yw < 1.0e-10 ||
00606       xr < 0.0  || yr < 0.0 || xg < 0.0 || yg < 0.0 || xb < 0.0 || yb < 0.0) {
00607     WARN("%s: Invalid cHRM chunk parameters found.", PNG_DEBUG_STR);
00608     return NULL;
00609   }
00610 
00611   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
00612       png_get_gAMA (png_ptr, info_ptr, &G)) {
00613     if (G < 1.0e-2) {
00614       WARN("%s: Unusual Gamma value: %g", PNG_DEBUG_STR, G);
00615       return NULL;
00616     }
00617     G = 1.0 / G; /* Gamma is inverted. */
00618   } else {
00619     G = 1.0;
00620   }
00621 
00622   cal_param = make_param_Cal(PNG_COLOR_TYPE_RGB, G, xw, yw, xr, yr, xg, yg, xb, yb);
00623 
00624   if (!cal_param)
00625     return NULL;
00626 
00627   colorspace = pdf_new_array();
00628   pdf_add_array(colorspace, pdf_new_name("CalRGB"));
00629   pdf_add_array(colorspace, cal_param);
00630 
00631   return colorspace;
00632 }
00633 
00634 static pdf_obj *
00635 create_cspace_CalGray (png_structp png_ptr, png_infop info_ptr)
00636 {
00637   pdf_obj *colorspace;
00638   pdf_obj *cal_param;
00639   double   xw, yw, xr, yr, xg, yg, xb, yb;
00640   double   G;
00641 
00642   if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM) ||
00643       !png_get_cHRM(png_ptr, info_ptr, &xw, &yw, &xr, &yr, &xg, &yg, &xb, &yb))
00644     return NULL;
00645 
00646   if (xw <= 0.0 || yw < 1.0e-10 ||
00647       xr < 0.0  || yr < 0.0 || xg < 0.0 || yg < 0.0 || xb < 0.0 || yb < 0.0) {
00648     WARN("%s: Invalid cHRM chunk parameters found.", PNG_DEBUG_STR);
00649     return NULL;
00650   }
00651 
00652   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
00653       png_get_gAMA (png_ptr, info_ptr, &G)) {
00654     if (G < 1.0e-2) {
00655       WARN("%s: Unusual Gamma value: %g", PNG_DEBUG_STR, G);
00656       return NULL;
00657     }
00658     G = 1.0 / G; /* Gamma is inverted. */
00659   } else {
00660     G = 1.0;
00661   }
00662 
00663   cal_param = make_param_Cal(PNG_COLOR_TYPE_GRAY, G, xw, yw, xr, yr, xg, yg, xb, yb);
00664 
00665   if (!cal_param)
00666     return NULL;
00667 
00668   colorspace = pdf_new_array();
00669   pdf_add_array(colorspace, pdf_new_name("CalGray"));
00670   pdf_add_array(colorspace, cal_param);
00671 
00672   return colorspace;
00673 }
00674 
00675 static pdf_obj *
00676 make_param_Cal (png_byte color_type,
00677               double G, /* Gamma */
00678               double xw, double yw,
00679               double xr, double yr, double xg, double yg, double xb, double yb)
00680 {
00681   pdf_obj *cal_param;
00682   pdf_obj *white_point, *matrix, *dev_gamma;
00683   double Xw, Yw, Zw; /* Yw = 1.0 */
00684   double Xr, Xg, Xb, Yr, Yb, Yg, Zr, Zg, Zb;
00685 
00686 #ifndef ABS
00687 #define ABS(x) ((x) < 0 ? -(x) : (x))
00688 #endif
00689   /*
00690    * TODO: Check validity
00691    *
00692    * Conversion found in
00693    *
00694    *  com.sixlegs.image.png - Java package to read and display PNG images
00695    *  Copyright (C) 1998, 1999, 2001 Chris Nokleberg
00696    *
00697    *  http://www.sixlegs.com/software/png/
00698    *
00699    */
00700   {
00701     double zw, zr, zg, zb;
00702     double fr, fg, fb;
00703     double det;
00704 
00705     /* WhitePoint */
00706     zw = 1 - (xw + yw);
00707     zr = 1 - (xr + yr); zg = 1 - (xg + yg); zb = 1 - (xb + yb);
00708     Xw = xw / yw; Yw = 1.0; Zw = zw / yw;
00709 
00710     /* Matrix */
00711     det = xr * (yg * zb - zg * yb) - xg * (yr * zb - zr * yb) + xb * (yr * zg - zr * yg);
00712     if (ABS(det) < 1.0e-10) {
00713       WARN("Non invertible matrix: Maybe invalid value(s) specified in cHRM chunk.");
00714       return NULL;
00715     }
00716     fr  = (Xw * (yg * zb - zg * yb) - xg * (zb - Zw * yb) + xb * (zg - Zw * yg)) / det;
00717     fg  = (xr * (zb - Zw * yb) - Xw * (yr * zb - zr * yb) + xb * (yr * Zw - zr)) / det;
00718     fb  = (xr * (yg * Zw - zg) - xg * (yr * Zw - zr) + Xw * (yr * zg - zr * yg)) / det;
00719     Xr = fr * xr; Yr = fr * yr; Zr = fr * zr;
00720     Xg = fg * xg; Yg = fg * yg; Zg = fg * zg;
00721     Xb = fb * xb; Yb = fb * yb; Zb = fb * zb;
00722   }
00723 
00724   if (G < 1.0e-2) {
00725     WARN("Unusual Gamma specified: %g", G);
00726     return NULL;
00727   }
00728 
00729   cal_param = pdf_new_dict();
00730 
00731   /* White point is always required. */
00732   white_point = pdf_new_array();
00733   pdf_add_array(white_point, pdf_new_number(ROUND(Xw, 0.00001)));
00734   pdf_add_array(white_point, pdf_new_number(ROUND(Yw, 0.00001)));
00735   pdf_add_array(white_point, pdf_new_number(ROUND(Zw, 0.00001)));
00736   pdf_add_dict(cal_param, pdf_new_name("WhitePoint"), white_point);
00737 
00738   /* Matrix - default: Identity */ 
00739   if (color_type & PNG_COLOR_MASK_COLOR) {
00740     if (G != 1.0) {
00741       dev_gamma = pdf_new_array();
00742       pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
00743       pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
00744       pdf_add_array(dev_gamma, pdf_new_number(ROUND(G, 0.00001)));
00745       pdf_add_dict(cal_param, pdf_new_name("Gamma"), dev_gamma);
00746     }
00747 
00748     matrix = pdf_new_array();
00749     pdf_add_array(matrix, pdf_new_number(ROUND(Xr, 0.00001)));
00750     pdf_add_array(matrix, pdf_new_number(ROUND(Yr, 0.00001)));
00751     pdf_add_array(matrix, pdf_new_number(ROUND(Zr, 0.00001)));
00752     pdf_add_array(matrix, pdf_new_number(ROUND(Xg, 0.00001)));
00753     pdf_add_array(matrix, pdf_new_number(ROUND(Yg, 0.00001)));
00754     pdf_add_array(matrix, pdf_new_number(ROUND(Zg, 0.00001)));
00755     pdf_add_array(matrix, pdf_new_number(ROUND(Xb, 0.00001)));
00756     pdf_add_array(matrix, pdf_new_number(ROUND(Yb, 0.00001)));
00757     pdf_add_array(matrix, pdf_new_number(ROUND(Zb, 0.00001)));
00758     pdf_add_dict (cal_param, pdf_new_name("Matrix"), matrix);
00759   } else { /* Gray */
00760     if (G != 1.0)
00761       pdf_add_dict(cal_param,
00762                  pdf_new_name("Gamma"),
00763                  pdf_new_number(ROUND(G, 0.00001)));
00764   }
00765 
00766   return cal_param;
00767 }
00768 
00769 /*
00770  * Set up Indexed ColorSpace for color-type PALETTE:
00771  *
00772  *  PNG allows only RGB color for base color space. If gAMA and/or cHRM
00773  *  chunk is available, we can use CalRGB color space instead of DeviceRGB
00774  *  for base color space.
00775  *
00776  */
00777 static pdf_obj *
00778 create_cspace_Indexed (png_structp png_ptr, png_infop info_ptr)
00779 {
00780   pdf_obj   *colorspace;
00781   pdf_obj   *base, *lookup;
00782   png_byte  *data_ptr;
00783   png_colorp plte;
00784   int        num_plte, i;
00785 
00786   if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE) ||
00787       !png_get_PLTE(png_ptr, info_ptr, &plte, &num_plte)) {
00788     WARN("%s: PNG does not have valid PLTE chunk.", PNG_DEBUG_STR);
00789     return NULL;
00790   }
00791 
00792   /* Order is important. */
00793   colorspace = pdf_new_array ();
00794   pdf_add_array(colorspace, pdf_new_name("Indexed"));
00795 
00796   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP))
00797     base = create_cspace_ICCBased(png_ptr, info_ptr);
00798   else {
00799     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB))
00800       base = create_cspace_sRGB(png_ptr, info_ptr);
00801     else
00802       base = create_cspace_CalRGB(png_ptr, info_ptr);
00803   }
00804 
00805   if (!base)
00806     base = pdf_new_name("DeviceRGB");
00807 
00808   pdf_add_array(colorspace, base);
00809   pdf_add_array(colorspace, pdf_new_number(num_plte-1));
00810   data_ptr = NEW(num_plte*3, png_byte);
00811   for (i = 0; i < num_plte; i++) {
00812     data_ptr[3*i]   = plte[i].red;
00813     data_ptr[3*i+1] = plte[i].green;
00814     data_ptr[3*i+2] = plte[i].blue;
00815   }
00816   lookup = pdf_new_string(data_ptr, num_plte*3);
00817   RELEASE(data_ptr);
00818   pdf_add_array(colorspace, lookup);
00819 
00820   return colorspace;
00821 }
00822 
00823 /*
00824  * pHYs: no support
00825  *
00826  *  pngimage.c is not responsible for adjusting image size.
00827  *  Higher layer must do something for this.
00828  */
00829 
00830 /*
00831  * Colorkey Mask: array
00832  *
00833  *  [component_0_min component_0_max ... component_n_min component_n_max]
00834  *
00835  */
00836 
00837 static pdf_obj *
00838 create_ckey_mask (png_structp png_ptr, png_infop info_ptr)
00839 {
00840   pdf_obj  *colorkeys;
00841   png_byte  color_type;
00842   png_bytep trans;
00843   int       num_trans, i;
00844   png_color_16p colors;
00845 
00846   if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
00847       !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &colors)) {
00848     WARN("%s: PNG does not have valid tRNS chunk!", PNG_DEBUG_STR);
00849     return NULL;
00850   }
00851 
00852   colorkeys  = pdf_new_array();
00853   color_type = png_get_color_type(png_ptr, info_ptr);
00854 
00855   switch (color_type) {
00856   case PNG_COLOR_TYPE_PALETTE:
00857     for (i = 0; i < num_trans; i++) {
00858       if (trans[i] == 0x00) {
00859        pdf_add_array(colorkeys, pdf_new_number(i));
00860        pdf_add_array(colorkeys, pdf_new_number(i));
00861       } else if (trans[i] != 0xff) {
00862        WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
00863       }
00864     }
00865     break;
00866   case PNG_COLOR_TYPE_RGB:
00867     pdf_add_array(colorkeys, pdf_new_number(colors->red));
00868     pdf_add_array(colorkeys, pdf_new_number(colors->red));
00869     pdf_add_array(colorkeys, pdf_new_number(colors->green));
00870     pdf_add_array(colorkeys, pdf_new_number(colors->green));
00871     pdf_add_array(colorkeys, pdf_new_number(colors->blue));
00872     pdf_add_array(colorkeys, pdf_new_number(colors->blue));
00873     break;
00874   case PNG_COLOR_TYPE_GRAY:
00875     pdf_add_array(colorkeys, pdf_new_number(colors->gray));
00876     pdf_add_array(colorkeys, pdf_new_number(colors->gray));
00877     break;
00878   default:
00879     WARN("%s: You found a bug in pngimage.c.", PNG_DEBUG_STR);
00880     pdf_release_obj(colorkeys);
00881     colorkeys = NULL;
00882   }
00883 
00884   return colorkeys;
00885 }
00886 
00887 /*
00888  * Soft-Mask: stream
00889  *
00890  *   <<
00891  *      /Type             /XObject
00892  *      /Subtype          /Image
00893  *      /Width            -int-
00894  *      /Height           -int-
00895  *      /BitsPerComponent bpc
00896  *   >>
00897  *   stream .... endstream
00898  *
00899  *   ColorSpace, Mask, SMask must be absent. ImageMask must be false or absent.
00900  */
00901 
00902 static pdf_obj *
00903 create_soft_mask (png_structp png_ptr, png_infop info_ptr,
00904                 png_bytep image_data_ptr, png_uint_32 width, png_uint_32 height)
00905 {
00906   pdf_obj    *smask, *dict;
00907   png_bytep   smask_data_ptr;
00908   png_bytep   trans;
00909   int         num_trans;
00910   png_uint_32 i;
00911 
00912   if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
00913       !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL)) {
00914     WARN("%s: PNG does not have valid tRNS chunk but tRNS is requested.", PNG_DEBUG_STR);
00915     return NULL;
00916   }
00917 
00918   smask = pdf_new_stream(STREAM_COMPRESS);
00919   dict  = pdf_stream_dict(smask);
00920   smask_data_ptr = (png_bytep) NEW(width*height, png_byte);
00921   pdf_add_dict(dict, pdf_new_name("Type"),    pdf_new_name("XObjcect"));
00922   pdf_add_dict(dict, pdf_new_name("Subtype"), pdf_new_name("Image"));
00923   pdf_add_dict(dict, pdf_new_name("Width"),      pdf_new_number(width));
00924   pdf_add_dict(dict, pdf_new_name("Height"),     pdf_new_number(height));
00925   pdf_add_dict(dict, pdf_new_name("ColorSpace"), pdf_new_name("DeviceGray"));
00926   pdf_add_dict(dict, pdf_new_name("BitsPerComponent"), pdf_new_number(8));
00927   for (i = 0; i < width*height; i++) {
00928     png_byte idx = image_data_ptr[i];
00929     smask_data_ptr[i] = (idx < num_trans) ? trans[idx] : 0xff;
00930   }
00931   pdf_add_stream(smask, (char *)smask_data_ptr, width*height);
00932   RELEASE(smask_data_ptr);
00933 
00934   return smask;
00935 }
00936 
00937 /* bitdepth is always 8 (16 is not supported) */
00938 static pdf_obj *
00939 strip_soft_mask (png_structp png_ptr, png_infop info_ptr,
00940                /* next two values will be modified. */
00941                png_bytep image_data_ptr, png_uint_32p rowbytes_ptr,
00942                png_uint_32 width, png_uint_32 height)
00943 {
00944   pdf_obj    *smask, *dict;
00945   png_byte    color_type;
00946   png_bytep   smask_data_ptr;
00947   png_uint_32 i;
00948 
00949   color_type = png_get_color_type(png_ptr, info_ptr);
00950 
00951   if (color_type & PNG_COLOR_MASK_COLOR) {
00952     if (*rowbytes_ptr != 4*width*sizeof(png_byte)) { /* Something wrong */
00953       WARN("%s: Inconsistent rowbytes value.", PNG_DEBUG_STR);
00954       return NULL;
00955     }
00956   } else {
00957     if (*rowbytes_ptr != 2*width*sizeof(png_byte)) { /* Something wrong */
00958       WARN("%s: Inconsistent rowbytes value.", PNG_DEBUG_STR);
00959       return NULL;
00960     }
00961   }
00962 
00963   smask = pdf_new_stream(STREAM_COMPRESS);
00964   dict  = pdf_stream_dict(smask);
00965   pdf_add_dict(dict, pdf_new_name("Type"),    pdf_new_name("XObjcect"));
00966   pdf_add_dict(dict, pdf_new_name("Subtype"), pdf_new_name("Image"));
00967   pdf_add_dict(dict, pdf_new_name("Width"),      pdf_new_number(width));
00968   pdf_add_dict(dict, pdf_new_name("Height"),     pdf_new_number(height));
00969   pdf_add_dict(dict, pdf_new_name("ColorSpace"), pdf_new_name("DeviceGray"));
00970   pdf_add_dict(dict, pdf_new_name("BitsPerComponent"), pdf_new_number(8));
00971 
00972   smask_data_ptr = (png_bytep) NEW(width*height, png_byte);
00973 
00974   switch (color_type) {
00975   case PNG_COLOR_TYPE_RGB_ALPHA:
00976     for (i = 0; i < width*height; i++) {
00977       memmove(image_data_ptr+(3*i), image_data_ptr+(4*i), 3);
00978       smask_data_ptr[i] = image_data_ptr[4*i+3];
00979     }
00980     *rowbytes_ptr = 3*width*sizeof(png_byte);
00981     break;
00982   case PNG_COLOR_TYPE_GRAY_ALPHA:
00983     for (i = 0; i < width*height; i++) {
00984       image_data_ptr[i] = image_data_ptr[2*i];
00985       smask_data_ptr[i] = image_data_ptr[2*i+1];
00986     }
00987     *rowbytes_ptr = width*sizeof(png_byte);
00988     break;
00989   default:
00990     WARN("You found a bug in pngimage.c!");
00991     pdf_release_obj(smask);
00992     RELEASE(smask_data_ptr);
00993     return NULL;
00994   }
00995 
00996   pdf_add_stream(smask, smask_data_ptr, width*height);
00997   RELEASE(smask_data_ptr);
00998 
00999   return smask;
01000 }
01001 
01002 static void
01003 read_image_data (png_structp png_ptr, png_infop info_ptr, /* info_ptr unused */
01004                png_bytep dest_ptr, png_uint_32 height, png_uint_32 rowbytes)
01005 {
01006   png_bytepp  rows_p;
01007   png_uint_32 i;
01008 
01009   rows_p = (png_bytepp) NEW (height, png_bytep);
01010   for (i=0; i< height; i++)
01011     rows_p[i] = dest_ptr + (rowbytes * i);
01012   png_read_image(png_ptr, rows_p);
01013   RELEASE(rows_p);
01014 }
01015 
01016 int
01017 png_get_bbox (FILE *png_file, long *width, long *height,
01018               double *xdensity, double *ydensity)
01019 {
01020   png_structp png_ptr;
01021   png_infop   png_info_ptr;
01022   png_uint_32 xppm, yppm;
01023 
01024   rewind (png_file);
01025   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
01026   if (png_ptr == NULL || 
01027       (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) {
01028     WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR);
01029     if (png_ptr)
01030       png_destroy_read_struct(&png_ptr, NULL, NULL);
01031     return -1;
01032   }
01033 
01034   /* Inititializing file IO. */
01035   png_init_io (png_ptr, png_file);
01036 
01037   /* Read PNG info-header and get some info. */
01038   png_read_info(png_ptr, png_info_ptr);
01039   *width      = png_get_image_width (png_ptr, png_info_ptr);
01040   *height     = png_get_image_height(png_ptr, png_info_ptr);
01041   xppm       = png_get_x_pixels_per_meter(png_ptr, png_info_ptr);
01042   yppm       = png_get_y_pixels_per_meter(png_ptr, png_info_ptr);
01043 
01044   /* Cleanup */
01045   if (png_info_ptr)
01046     png_destroy_info_struct(png_ptr, &png_info_ptr);
01047   if (png_ptr)
01048     png_destroy_read_struct(&png_ptr, NULL, NULL);
01049 
01050   if (compat_mode)
01051     *xdensity = *ydensity = 72.0 / 100.0;
01052   else {
01053     *xdensity = xppm ? 72.0 / 0.0254 / xppm : 1.0;
01054     *ydensity = yppm ? 72.0 / 0.0254 / yppm : 1.0;
01055   }
01056 
01057   return 0;
01058 }
01059 
01060 #endif /* HAVE_LIBPNG */