% writepng.w % Copyright 1996-2006 Han The Thanh % Copyright 2006-2010 Taco Hoekwater % This file is part of LuaTeX. % LuaTeX 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. % LuaTeX 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 Lesser General Public % License for more details. % You should have received a copy of the GNU General Public License along % with LuaTeX; if not, see . @ @c static const char _svn_version[] = "$Id: writepng.w 4259 2011-05-12 20:46:59Z hhenkel $ " "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.70.1/source/texk/web2c/luatexdir/image/writepng.w $"; #include #include "ptexlib.h" #include "image/image.h" #include "image/writepng.h" @ @c static int transparent_page_group = -1; static void close_and_cleanup_png(image_dict * idict) { assert(idict != NULL); assert(img_file(idict) != NULL); assert(img_filepath(idict) != NULL); xfclose(img_file(idict), img_filepath(idict)); img_file(idict) = NULL; assert(img_png_ptr(idict) != NULL); png_destroy_read_struct(&(img_png_png_ptr(idict)), &(img_png_info_ptr(idict)), NULL); xfree(img_png_ptr(idict)); } @ @c void read_png_info(PDF pdf, image_dict * idict, img_readtype_e readtype) { png_structp png_p; png_infop info_p; assert(idict != NULL); assert(img_type(idict) == IMG_TYPE_PNG); img_totalpages(idict) = 1; img_pagenum(idict) = 1; img_xres(idict) = img_yres(idict) = 0; assert(img_file(idict) == NULL); img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); assert(img_png_ptr(idict) == NULL); img_png_ptr(idict) = xtalloc(1, png_img_struct); if ((png_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL) pdftex_fail("libpng: png_create_read_struct() failed"); img_png_png_ptr(idict) = png_p; if ((info_p = png_create_info_struct(png_p)) == NULL) pdftex_fail("libpng: png_create_info_struct() failed"); img_png_info_ptr(idict) = info_p; if (setjmp(png_jmpbuf(png_p))) pdftex_fail("libpng: internal error"); png_init_io(png_p, img_file(idict)); png_read_info(png_p, info_p); /* resolution support */ img_xsize(idict) = (int) png_get_image_width(png_p, info_p); img_ysize(idict) = (int) png_get_image_height(png_p, info_p); if (png_get_valid(png_p, info_p, PNG_INFO_pHYs)) { img_xres(idict) = round(0.0254 * (double) png_get_x_pixels_per_meter(png_p, info_p)); img_yres(idict) = round(0.0254 * (double) png_get_y_pixels_per_meter(png_p, info_p)); } switch (png_get_color_type(png_p, info_p)) { case PNG_COLOR_TYPE_PALETTE: img_procset(idict) |= PROCSET_IMAGE_C | PROCSET_IMAGE_I; break; case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: img_procset(idict) |= PROCSET_IMAGE_B; break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: img_procset(idict) |= PROCSET_IMAGE_C; break; default: pdftex_fail("unsupported type of color_type <%i>", (int) png_get_color_type(png_p, info_p)); } img_colordepth(idict) = png_get_bit_depth(png_p, info_p); if (readtype == IMG_CLOSEINBETWEEN) close_and_cleanup_png(idict); } @ @c #define write_gray_pixel_16(r) \ if (j % 4 == 0||j % 4 == 1) pdf_quick_out(pdf,*r++); \ else smask[smask_ptr++] = *r++ #define write_gray_pixel_8(r) \ if (j % 2 == 0) pdf_quick_out(pdf,*r++); \ else smask[smask_ptr++] = *r++ #define write_rgb_pixel_16(r) \ if (!(j % 8 == 6||j % 8 == 7)) pdf_quick_out(pdf,*r++); \ else smask[smask_ptr++] = *r++ #define write_rgb_pixel_8(r) \ if (j % 4 != 3) pdf_quick_out(pdf,*r++); \ else smask[smask_ptr++] = *r++ #define write_simple_pixel(r) pdf_quick_out(pdf,*r++) #define write_noninterlaced(outmac) \ for (i = 0; i < (int)png_get_image_height (png_p, info_p); i++) { \ png_read_row(png_p, row, NULL); \ r = row; \ k = (int)png_get_rowbytes(png_p, info_p); \ while(k > 0) { \ l = (k > pdf->buf_size)? pdf->buf_size : k; \ pdf_room(pdf,l); \ for (j = 0; j < l; j++) { \ outmac; \ } \ k -= l; \ } \ } #define write_interlaced(outmac) \ for (i = 0; i < (int)png_get_image_height (png_p, info_p); i++) { \ row = rows[i]; \ k = (int)png_get_rowbytes(png_p, info_p); \ while(k > 0) { \ l = (k > pdf->buf_size)?pdf->buf_size: k;\ pdf_room(pdf,l); \ for (j = 0; j < l; j++) { \ outmac; \ } \ k -= l; \ } \ xfree(rows[i]); \ } @ @c static void write_png_palette(PDF pdf, image_dict * idict) { int i, j, k, l; png_structp png_p = img_png_png_ptr(idict); png_infop info_p = img_png_info_ptr(idict); png_bytep row, r, *rows; int palette_objnum = 0; png_colorp palette; int num_palette; png_get_PLTE(png_p, info_p, &palette, &num_palette); if (img_colorspace(idict) != 0) { pdf_printf(pdf, "%i 0 R\n", (int) img_colorspace(idict)); } else { palette_objnum = pdf_create_obj(pdf, obj_type_others, 0); pdf_printf(pdf, "[/Indexed /DeviceRGB %i %i 0 R]\n", (int) (num_palette - 1), (int) palette_objnum); } pdf_begin_stream(pdf); if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); write_noninterlaced(write_simple_pixel(r)); xfree(row); } else { if (png_get_image_height (png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) pdftex_warn ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); rows = xtalloc(png_get_image_height (png_p, info_p), png_bytep); for (i = 0; (unsigned) i < png_get_image_height (png_p, info_p); i++) rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); png_read_image(png_p, rows); write_interlaced(write_simple_pixel(row)); xfree(rows); } pdf_end_stream(pdf); if (palette_objnum > 0) { pdf_begin_dict(pdf, palette_objnum, 0); pdf_begin_stream(pdf); for (i = 0; i < num_palette; i++) { pdf_room(pdf, 3); pdf_quick_out(pdf, palette[i].red); pdf_quick_out(pdf, palette[i].green); pdf_quick_out(pdf, palette[i].blue); } pdf_end_stream(pdf); } } @ @c static void write_png_gray(PDF pdf, image_dict * idict) { int i, j, k, l; png_structp png_p = img_png_png_ptr(idict); png_infop info_p = img_png_info_ptr(idict); png_bytep row, r, *rows; if (img_colorspace(idict) != 0) { pdf_printf(pdf, "%i 0 R\n", (int) img_colorspace(idict)); } else { pdf_puts(pdf, "/DeviceGray\n"); } pdf_begin_stream(pdf); if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); write_noninterlaced(write_simple_pixel(r)); xfree(row); } else { if (png_get_image_height (png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) pdftex_warn ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); rows = xtalloc(png_get_image_height (png_p, info_p), png_bytep); for (i = 0; (unsigned) i < png_get_image_height (png_p, info_p); i++) rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); png_read_image(png_p, rows); write_interlaced(write_simple_pixel(row)); xfree(rows); } pdf_end_stream(pdf); } @ @c static void write_png_gray_alpha(PDF pdf, image_dict * idict) { int i, j, k, l; png_structp png_p = img_png_png_ptr(idict); png_infop info_p = img_png_info_ptr(idict); png_bytep row, r, *rows; int smask_objnum = 0; png_bytep smask; int smask_ptr = 0; int smask_size = 0; png_byte bitdepth; if (img_colorspace(idict) != 0) { pdf_printf(pdf, "%i 0 R\n", (int) img_colorspace(idict)); } else { pdf_puts(pdf, "/DeviceGray\n"); } smask_objnum = pdf_create_obj(pdf, obj_type_others, 0); pdf_printf(pdf, "/SMask %i 0 R\n", (int) smask_objnum); smask_size = (int) ((png_get_rowbytes(png_p, info_p) / 2) * png_get_image_height (png_p, info_p)); smask = xtalloc((unsigned) smask_size, png_byte); pdf_begin_stream(pdf); if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { write_noninterlaced(write_gray_pixel_16(r)); } else { write_noninterlaced(write_gray_pixel_8(r)); } xfree(row); } else { if (png_get_image_height (png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) pdftex_warn ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); rows = xtalloc(png_get_image_height (png_p, info_p), png_bytep); for (i = 0; (unsigned) i < png_get_image_height (png_p, info_p); i++) rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); png_read_image(png_p, rows); if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { write_interlaced(write_gray_pixel_16(row)); } else { write_interlaced(write_gray_pixel_8(row)); } xfree(rows); } pdf_end_stream(pdf); pdf_flush(pdf); /* now write the Smask object */ bitdepth = png_get_bit_depth (png_p, info_p); pdf_begin_dict(pdf, smask_objnum, 0); pdf_puts(pdf, "/Type /XObject\n/Subtype /Image\n"); if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) pdf_printf(pdf, "%s\n", img_attr(idict)); pdf_printf(pdf, "/Width %i\n/Height %i\n/BitsPerComponent %i\n", (int) png_get_image_width (png_p, info_p), (int) png_get_image_height (png_p, info_p), (bitdepth == 16 ? 8 : bitdepth)); pdf_puts(pdf, "/ColorSpace /DeviceGray\n"); pdf_begin_stream(pdf); for (i = 0; i < smask_size; i++) { if (i % 8 == 0) pdf_room(pdf, 8); pdf_quick_out(pdf, smask[i]); if (bitdepth == 16) i++; } pdf_end_stream(pdf); xfree(smask); } @ @c static void write_png_rgb(PDF pdf, image_dict * idict) { int i, j, k, l; png_structp png_p = img_png_png_ptr(idict); png_infop info_p = img_png_info_ptr(idict); png_bytep row, r, *rows; if (img_colorspace(idict) != 0) { pdf_printf(pdf, "%i 0 R\n", (int) img_colorspace(idict)); } else { pdf_puts(pdf, "/DeviceRGB\n"); } pdf_begin_stream(pdf); if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); write_noninterlaced(write_simple_pixel(r)); xfree(row); } else { if (png_get_image_height (png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) pdftex_warn ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); rows = xtalloc(png_get_image_height (png_p, info_p), png_bytep); for (i = 0; (unsigned) i < png_get_image_height (png_p, info_p); i++) rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); png_read_image(png_p, rows); write_interlaced(write_simple_pixel(row)); xfree(rows); } pdf_end_stream(pdf); } @ @c static void write_png_rgb_alpha(PDF pdf, image_dict * idict) { int i, j, k, l; png_structp png_p = img_png_png_ptr(idict); png_infop info_p = img_png_info_ptr(idict); png_bytep row, r, *rows; int smask_objnum = 0; png_bytep smask; int smask_ptr = 0; int smask_size = 0; png_byte bitdepth; if (img_colorspace(idict) != 0) { pdf_printf(pdf, "%i 0 R\n", (int) img_colorspace(idict)); } else { pdf_puts(pdf, "/DeviceRGB\n"); } smask_objnum = pdf_create_obj(pdf, obj_type_others, 0); pdf_printf(pdf, "/SMask %i 0 R\n", (int) smask_objnum); smask_size = (int) ((png_get_rowbytes (png_p, info_p) / 4) * png_get_image_height (png_p, info_p)); smask = xtalloc((unsigned) smask_size, png_byte); pdf_begin_stream(pdf); if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { write_noninterlaced(write_rgb_pixel_16(r)); } else { write_noninterlaced(write_rgb_pixel_8(r)); } xfree(row); } else { if (png_get_image_height (png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) pdftex_warn ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); rows = xtalloc(png_get_image_height (png_p, info_p), png_bytep); for (i = 0; (unsigned) i < png_get_image_height (png_p, info_p); i++) rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); png_read_image(png_p, rows); if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { write_interlaced(write_rgb_pixel_16(row)); } else { write_interlaced(write_rgb_pixel_8(row)); } xfree(rows); } pdf_end_stream(pdf); pdf_flush(pdf); /* now write the Smask object */ if (smask_objnum > 0) { bitdepth = png_get_bit_depth (png_p, info_p); pdf_begin_dict(pdf, smask_objnum, 0); pdf_puts(pdf, "/Type /XObject\n/Subtype /Image\n"); if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) pdf_printf(pdf, "%s\n", img_attr(idict)); pdf_printf(pdf, "/Width %i\n/Height %i\n/BitsPerComponent %i\n", (int) png_get_image_width (png_p, info_p), (int) png_get_image_height (png_p, info_p), (bitdepth == 16 ? 8 : bitdepth)); pdf_puts(pdf, "/ColorSpace /DeviceGray\n"); pdf_begin_stream(pdf); for (i = 0; i < smask_size; i++) { if (i % 8 == 0) pdf_room(pdf, 8); pdf_quick_out(pdf, smask[i]); if (bitdepth == 16) i++; } xfree(smask); pdf_end_stream(pdf); } } @ The |copy_png| function is from Hartmut Henkel. The goal is to use pdf's native FlateDecode support if that is possible. Only a subset of the png files allows this, but when possible it greatly improves inclusion speed. Code cheerfully gleaned from Thomas Merz' PDFlib, file |p_png.c| "SPNG - Simple PNG" @c static int spng_getint(FILE * fp) { unsigned char buf[4]; if (fread(buf, 1, 4, fp) != 4) pdftex_fail("writepng: reading chunk type failed"); return ((((((int) buf[0] << 8) + buf[1]) << 8) + buf[2]) << 8) + buf[3]; } #define SPNG_CHUNK_IDAT 0x49444154 #define SPNG_CHUNK_IEND 0x49454E44 static void copy_png(PDF pdf, image_dict * idict) { png_structp png_p; png_infop info_p; FILE *fp; int i, len, type, streamlength = 0; boolean endflag = false; int idat = 0; /* flag to check continuous IDAT chunks sequence */ assert(idict != NULL); png_p = img_png_png_ptr(idict); info_p = img_png_info_ptr(idict); fp = (FILE *) png_get_io_ptr(png_p); /* 1st pass to find overall stream /Length */ if (fseek(fp, 8, SEEK_SET) != 0) pdftex_fail("writepng: fseek in PNG file failed"); do { len = spng_getint(fp); type = spng_getint(fp); switch (type) { case SPNG_CHUNK_IEND: endflag = true; break; case SPNG_CHUNK_IDAT: streamlength += len; default: if (fseek(fp, len + 4, SEEK_CUR) != 0) pdftex_fail("writepng: fseek in PNG file failed"); } } while (endflag == false); pdf_printf(pdf, "/Length %d\n" "/Filter/FlateDecode\n" "/DecodeParms<<" "/Colors %d" "/Columns %u" "/BitsPerComponent %i" "/Predictor 10>>\n>>\nstream\n", streamlength, png_get_color_type (png_p, info_p) == 2 ? 3 : 1, png_get_image_width (png_p, info_p), png_get_bit_depth (png_p, info_p)); /* 2nd pass to copy data */ endflag = false; if (fseek(fp, 8, SEEK_SET) != 0) pdftex_fail("writepng: fseek in PNG file failed"); do { len = spng_getint(fp); type = spng_getint(fp); switch (type) { case SPNG_CHUNK_IDAT: /* do copy */ if (idat == 2) pdftex_fail("writepng: IDAT chunk sequence broken"); idat = 1; while (len > 0) { i = (len > pdf->buf_size) ? pdf->buf_size : len; pdf_room(pdf, i); fread(&pdf->buf[pdf->ptr], 1, (size_t) i, fp); pdf->ptr += i; len -= i; } if (fseek(fp, 4, SEEK_CUR) != 0) pdftex_fail("writepng: fseek in PNG file failed"); break; case SPNG_CHUNK_IEND: /* done */ pdf_end_stream(pdf); endflag = true; break; default: if (idat == 1) idat = 2; if (fseek(fp, len + 4, SEEK_CUR) != 0) pdftex_fail("writepng: fseek in PNG file failed"); } } while (endflag == false); } @ @c static void reopen_png(PDF pdf, image_dict * idict) { int width, height, xres, yres; width = img_xsize(idict); /* do consistency check */ height = img_ysize(idict); xres = img_xres(idict); yres = img_yres(idict); read_png_info(pdf, idict, IMG_KEEPOPEN); if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) pdftex_fail("writepng: image dimensions have changed"); } @ @c static boolean last_png_needs_page_group; void write_png(PDF pdf, image_dict * idict) { boolean png_copy = true; double gamma = 0.0; png_fixed_point int_file_gamma = 0; #ifndef PNG_FP_1 /* for libpng < 1.5.0 */ #define PNG_FP_1 100000 #endif int i; int palette_objnum = 0; png_structp png_p; png_infop info_p; assert(idict != NULL); last_png_needs_page_group = false; if (img_file(idict) == NULL) reopen_png(pdf, idict); assert(img_png_ptr(idict) != NULL); png_p = img_png_png_ptr(idict); info_p = img_png_info_ptr(idict); if (pdf->minor_version < 5) pdf->image_hicolor = 0; pdf_puts(pdf, "/Type /XObject\n/Subtype /Image\n"); if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) pdf_printf(pdf, "%s\n", img_attr(idict)); /* simple transparency support */ if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_p); png_copy = false; } /* alpha channel support */ if (pdf->minor_version < 4 && png_get_color_type(png_p, info_p) | PNG_COLOR_MASK_ALPHA) { png_set_strip_alpha(png_p); png_copy = false; } /* 16 bit depth support */ if (pdf->minor_version < 5) pdf->image_hicolor = 0; if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor == 0)) { png_set_strip_16(png_p); png_copy = false; } /* gamma support */ if (png_get_valid(png_p, info_p, PNG_INFO_gAMA)) { png_get_gAMA(png_p, info_p, &gamma); png_get_gAMA_fixed(png_p, info_p, &int_file_gamma); } if (pdf->image_apply_gamma) { if (png_get_valid(png_p, info_p, PNG_INFO_gAMA)) png_set_gamma(png_p, (pdf->gamma / 1000.0), gamma); else png_set_gamma(png_p, (pdf->gamma / 1000.0), (1000.0 / pdf->image_gamma)); png_copy = false; } /* reset structure */ (void) png_set_interlace_handling(png_p); png_read_update_info(png_p, info_p); pdf_printf(pdf, "/Width %i\n/Height %i\n/BitsPerComponent %i\n", (int) png_get_image_width(png_p, info_p), (int) png_get_image_height(png_p, info_p), (int) png_get_bit_depth(png_p, info_p)); pdf_puts(pdf, "/ColorSpace "); if (png_copy && pdf->minor_version > 1 && png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE && (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY || png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB) && !pdf->image_apply_gamma && (!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1) && !png_get_valid(png_p, info_p, PNG_INFO_cHRM) && !png_get_valid(png_p, info_p, PNG_INFO_iCCP) && !png_get_valid(png_p, info_p, PNG_INFO_sBIT) && !png_get_valid(png_p, info_p, PNG_INFO_sRGB) && !png_get_valid(png_p, info_p, PNG_INFO_bKGD) && !png_get_valid(png_p, info_p, PNG_INFO_hIST) && !png_get_valid(png_p, info_p, PNG_INFO_tRNS) && !png_get_valid(png_p, info_p, PNG_INFO_sPLT) ) { /* Copy PNG */ png_colorp palette; int num_palette; png_get_PLTE(png_p, info_p, &palette, &num_palette); if (img_colorspace(idict) != 0) { pdf_printf(pdf, "%i 0 R\n", (int) img_colorspace(idict)); } else { switch (png_get_color_type(png_p, info_p)) { case PNG_COLOR_TYPE_PALETTE: palette_objnum = pdf_create_obj(pdf, obj_type_others, 0); pdf_printf(pdf, "[/Indexed /DeviceRGB %i %i 0 R]\n", num_palette - 1, (int) palette_objnum); break; case PNG_COLOR_TYPE_GRAY: pdf_puts(pdf, "/DeviceGray\n"); break; default: /* RGB */ pdf_puts(pdf, "/DeviceRGB\n"); }; } if (tracefilenames) tex_printf(" (PNG copy)"); copy_png(pdf, idict); if (palette_objnum > 0) { pdf_begin_dict(pdf, palette_objnum, 0); pdf_begin_stream(pdf); for (i = 0; i < num_palette; i++) { pdf_room(pdf, 3); pdf_quick_out(pdf, palette[i].red); pdf_quick_out(pdf, palette[i].green); pdf_quick_out(pdf, palette[i].blue); } pdf_end_stream(pdf); } } else { if (0) { tex_printf(" *** PNG copy skipped because: "); if (!png_copy) tex_printf("!png_copy "); if (!(pdf->minor_version > 1)) tex_printf("minorversion=%d ", pdf->minor_version); if (!(png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE)) tex_printf("interlaced "); if (!((png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY) || (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB))) tex_printf("colortype "); if (pdf->image_apply_gamma) tex_printf("apply gamma "); if (!(!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1)) tex_printf("gamma "); if (png_get_valid(png_p, info_p, PNG_INFO_cHRM)) tex_printf("cHRM "); if (png_get_valid(png_p, info_p, PNG_INFO_iCCP)) tex_printf("iCCP "); if (png_get_valid(png_p, info_p, PNG_INFO_sBIT)) tex_printf("sBIT "); if (png_get_valid(png_p, info_p, PNG_INFO_sRGB)) tex_printf("sRGB "); if (png_get_valid(png_p, info_p, PNG_INFO_bKGD)) tex_printf("bKGD "); if (png_get_valid(png_p, info_p, PNG_INFO_hIST)) tex_printf("hIST "); if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) tex_printf("tRNS "); if (png_get_valid(png_p, info_p, PNG_INFO_sPLT)) tex_printf("sPLT "); } switch (png_get_color_type(png_p, info_p)) { case PNG_COLOR_TYPE_PALETTE: write_png_palette(pdf, idict); break; case PNG_COLOR_TYPE_GRAY: write_png_gray(pdf, idict); break; case PNG_COLOR_TYPE_GRAY_ALPHA: if (pdf->minor_version >= 4) { write_png_gray_alpha(pdf, idict); last_png_needs_page_group = true; } else write_png_gray(pdf, idict); break; case PNG_COLOR_TYPE_RGB: write_png_rgb(pdf, idict); break; case PNG_COLOR_TYPE_RGB_ALPHA: if (pdf->minor_version >= 4) { write_png_rgb_alpha(pdf, idict); last_png_needs_page_group = true; } else write_png_rgb(pdf, idict); break; default: pdftex_fail("unsupported type of color_type <%i>", png_get_color_type(png_p, info_p)); } } pdf_flush(pdf); close_and_cleanup_png(idict); } @ @c static boolean transparent_page_group_was_written = false; @ Called after the xobject generated by |write_png| has been finished; used to write out additional objects @c void write_additional_png_objects(PDF pdf) { (void) pdf; (void) transparent_page_group; (void) transparent_page_group_was_written; return; /* this interferes with current macro-based usage and cannot be configured */ #if 0 if (last_png_needs_page_group) { if (!transparent_page_group_was_written && transparent_page_group > 1) { /* create new group object */ transparent_page_group_was_written = true; pdf_begin_obj(pdf, transparent_page_group, 2); if (pdf->compress_level == 0) { pdf_puts(pdf, "%PTEX Group needed for transparent pngs\n"); } pdf_puts (pdf, "<>\n"); pdf_end_obj(pdf); } } #endif }