1 /*
2 Copyright 1996-2014 Han The Thanh, <thanh@pdftex.org>
3 
4 This file is part of pdfTeX.
5 
6 pdfTeX is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 pdfTeX is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "ptexlib.h"
21 #include "image.h"
22 
23 static int transparent_page_group = 0;
24 
warn(png_structp png_ptr,png_const_charp msg)25 static void warn(png_structp png_ptr, png_const_charp msg)
26 {
27   (void)png_ptr; (void)msg; /* Make compiler happy */
28 }
29 
read_png_info(integer img)30 void read_png_info(integer img)
31 {
32     FILE *png_file = xfopen(img_name(img), FOPEN_RBIN_MODE);
33 
34     if ((png_ptr(img) = png_create_read_struct(PNG_LIBPNG_VER_STRING,
35                                                NULL, NULL, warn)) == NULL)
36         pdftex_fail("libpng: png_create_read_struct() failed");
37     if ((png_info(img) = png_create_info_struct(png_ptr(img))) == NULL)
38         pdftex_fail("libpng: png_create_info_struct() failed");
39     if (setjmp(png_jmpbuf(png_ptr(img))))
40         pdftex_fail("libpng: internal error");
41 #if PNG_LIBPNG_VER >= 10603
42     /* ignore possibly incorrect CMF bytes */
43     png_set_option(png_ptr(img), PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
44 #endif
45     png_init_io(png_ptr(img), png_file);
46     png_read_info(png_ptr(img), png_info(img));
47     /* resolution support */
48     img_width(img) = png_get_image_width(png_ptr(img), png_info(img));
49     img_height(img) = png_get_image_height(png_ptr(img), png_info(img));
50     if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_pHYs)) {
51         img_xres(img) =
52             round(0.0254 *
53                   png_get_x_pixels_per_meter(png_ptr(img), png_info(img)));
54         img_yres(img) =
55             round(0.0254 *
56                   png_get_y_pixels_per_meter(png_ptr(img), png_info(img)));
57     }
58     switch (png_get_color_type(png_ptr(img), png_info(img))) {
59     case PNG_COLOR_TYPE_PALETTE:
60         img_color(img) = IMAGE_COLOR_C | IMAGE_COLOR_I;
61         break;
62     case PNG_COLOR_TYPE_GRAY:
63     case PNG_COLOR_TYPE_GRAY_ALPHA:
64         img_color(img) = IMAGE_COLOR_B;
65         break;
66     case PNG_COLOR_TYPE_RGB:
67     case PNG_COLOR_TYPE_RGB_ALPHA:
68         img_color(img) = IMAGE_COLOR_C;
69         break;
70     default:
71         pdftex_fail("unsupported type of color_type <%i>",
72                     png_get_color_type(png_ptr(img), png_info(img)));
73     }
74     if (fixedpdfminorversion >= 4
75         && (png_get_color_type(png_ptr(img), png_info(img)) == PNG_COLOR_TYPE_GRAY_ALPHA
76             || png_get_color_type(png_ptr(img), png_info(img)) == PNG_COLOR_TYPE_RGB_ALPHA)) {
77         /* png with alpha channel in device colours; we have to add a Page
78          * Group to make Adobe happy, so we have to create a dummy group object
79          */
80         if (transparent_page_group == 0) {
81             transparent_page_group = pdfnewobjnum();
82         }
83         if (pdfpagegroupval == 0) {
84             pdfpagegroupval = transparent_page_group;
85         }
86         img_group_ref(img) = pdfpagegroupval;
87     }
88 }
89 
90 
91 #define write_gray_pixel_16(r)                           \
92   if (j % 4 == 0||j % 4 == 1)  pdfbuf[pdfptr++]  = *r++; \
93   else                        smask[smask_ptr++] = *r++
94 
95 #define write_gray_pixel_8(r)                   \
96     if (j % 2 == 0)  pdfbuf[pdfptr++]   = *r++; \
97     else             smask[smask_ptr++] = *r++
98 
99 
100 #define write_rgb_pixel_16(r)                                 \
101     if (!(j % 8 == 6||j % 8 == 7)) pdfbuf[pdfptr++]  = *r++;  \
102     else                           smask[smask_ptr++] = *r++
103 
104 #define write_rgb_pixel_8(r)                                 \
105     if (j % 4 != 3)      pdfbuf[pdfptr++]  = *r++;           \
106     else                 smask[smask_ptr++] = *r++
107 
108 #define write_simple_pixel(r)    pdfbuf[pdfptr++] = *r++
109 
110 
111 #define write_noninterlaced(outmac)                    \
112   for (i = 0; i < (int)png_get_image_height(png_ptr(img), png_info(img)); i++) {   \
113     png_read_row(png_ptr(img), row, NULL);             \
114     r = row;                                           \
115     k = png_get_rowbytes(png_ptr(img), png_info(img)); \
116     while(k > 0) {                                     \
117         l = (k > pdfbufsize)? pdfbufsize : k;          \
118                 pdfroom(l);                            \
119                 for (j = 0; j < l; j++) {              \
120                   outmac;                              \
121                 }                                      \
122                 k -= l;                                \
123             }                                          \
124         }
125 
126 #define write_interlaced(outmac)                       \
127   for (i = 0; i < (int)png_get_image_height(png_ptr(img), png_info(img)); i++) {   \
128             row = rows[i];                             \
129             k = png_get_rowbytes(png_ptr(img), png_info(img));  \
130             while(k > 0) {                             \
131                 l = (k > pdfbufsize)? pdfbufsize : k;  \
132                 pdfroom(l);                            \
133                 for (j = 0; j < l; j++) {              \
134                   outmac;                              \
135                 }                                      \
136                 k -= l;                                \
137             }                                          \
138             xfree(rows[i]);                            \
139         }
140 
141 
write_png_palette(integer img)142 static void write_png_palette(integer img)
143 {
144     int i, j, k, l;
145     png_bytep row, r, *rows;
146     integer palette_objnum = 0;
147     png_colorp palette;
148     int num_palette;
149 
150     png_get_PLTE(png_ptr(img), png_info(img), &palette, &num_palette);
151 
152     pdfcreateobj(0, 0);
153     palette_objnum = objptr;
154     if (img_colorspace_ref(img) != 0) {
155         pdf_printf("%i 0 R\n", (int) img_colorspace_ref(img));
156     } else {
157         pdf_printf("[/Indexed /DeviceRGB %i %i 0 R]\n",
158                    num_palette -1, (int) palette_objnum);
159     }
160     pdfbeginstream();
161     if (png_get_interlace_type(png_ptr(img), png_info(img)) == PNG_INTERLACE_NONE) {
162         row = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
163         write_noninterlaced(write_simple_pixel(r));
164         xfree(row);
165     } else {
166         if (png_get_image_height(png_ptr(img), png_info(img))
167             * png_get_rowbytes(png_ptr(img), png_info(img)) >= 10240000L)
168             pdftex_warn
169                 ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)");
170         rows = xtalloc(png_get_image_height(png_ptr(img), png_info(img)), png_bytep);
171         for (i = 0; (unsigned) i < png_get_image_height(png_ptr(img), png_info(img)); i++)
172             rows[i] = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
173         png_read_image(png_ptr(img), rows);
174         write_interlaced(write_simple_pixel(row));
175         xfree(rows);
176     }
177     pdfendstream();
178     if (palette_objnum > 0) {
179         pdfbegindict(palette_objnum, 0);
180         pdfbeginstream();
181         for (i = 0; (unsigned) i < num_palette; i++) {
182             pdfroom(3);
183             pdfbuf[pdfptr++] = palette[i].red;
184             pdfbuf[pdfptr++] = palette[i].green;
185             pdfbuf[pdfptr++] = palette[i].blue;
186         }
187         pdfendstream();
188     }
189 }
190 
write_png_gray(integer img)191 static void write_png_gray(integer img)
192 {
193     int i, j, k, l;
194     png_bytep row, r, *rows;
195     if (img_colorspace_ref(img) != 0) {
196         pdf_printf("%i 0 R\n", (int) img_colorspace_ref(img));
197     } else {
198         pdf_puts("/DeviceGray\n");
199     }
200     pdfbeginstream();
201     if (png_get_interlace_type(png_ptr(img), png_info(img)) == PNG_INTERLACE_NONE) {
202         row = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
203         write_noninterlaced(write_simple_pixel(r));
204         xfree(row);
205     } else {
206         if (png_get_image_height(png_ptr(img), png_info(img))
207             * png_get_rowbytes(png_ptr(img), png_info(img)) >= 10240000L)
208             pdftex_warn
209                 ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)");
210         rows = xtalloc(png_get_image_height(png_ptr(img), png_info(img)), png_bytep);
211         for (i = 0; (unsigned) i < png_get_image_height(png_ptr(img), png_info(img)); i++)
212             rows[i] = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
213         png_read_image(png_ptr(img), rows);
214         write_interlaced(write_simple_pixel(row));
215         xfree(rows);
216     }
217     pdfendstream();
218 }
219 
220 
221 
write_png_gray_alpha(integer img)222 static void write_png_gray_alpha(integer img)
223 {
224     int i, j, k, l;
225     png_bytep row, r, *rows;
226     integer smask_objnum = 0;
227     png_bytep smask;
228     integer smask_ptr = 0;
229     integer smask_size = 0;
230     int bitdepth;
231     if (img_colorspace_ref(img) != 0) {
232         pdf_printf("%i 0 R\n", (int) img_colorspace_ref(img));
233     } else {
234         pdf_puts("/DeviceGray\n");
235     }
236     pdfcreateobj(0, 0);
237     smask_objnum = objptr;
238     pdf_printf("/SMask %i 0 R\n", (int) smask_objnum);
239     smask_size = (png_get_rowbytes(png_ptr(img), png_info(img)) / 2)
240                  * png_get_image_height(png_ptr(img), png_info(img));
241     smask = xtalloc(smask_size, png_byte);
242     pdfbeginstream();
243     if (png_get_interlace_type(png_ptr(img), png_info(img)) == PNG_INTERLACE_NONE) {
244         row = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
245         if ((png_get_bit_depth(png_ptr(img), png_info(img)) == 16) && fixedimagehicolor) {
246             write_noninterlaced(write_gray_pixel_16(r));
247         } else {
248             write_noninterlaced(write_gray_pixel_8(r));
249         }
250         xfree(row);
251     } else {
252         if (png_get_image_height(png_ptr(img), png_info(img))
253             * png_get_rowbytes(png_ptr(img), png_info(img)) >= 10240000L)
254             pdftex_warn
255                 ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)");
256         rows = xtalloc(png_get_image_height(png_ptr(img), png_info(img)), png_bytep);
257         for (i = 0; (unsigned) i < png_get_image_height(png_ptr(img), png_info(img)); i++)
258             rows[i] = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
259         png_read_image(png_ptr(img), rows);
260         if ((png_get_bit_depth(png_ptr(img), png_info(img)) == 16) && fixedimagehicolor) {
261             write_interlaced(write_gray_pixel_16(row));
262         } else {
263             write_interlaced(write_gray_pixel_8(row));
264         }
265         xfree(rows);
266     }
267     pdfendstream();
268     pdfflush();
269     /* now write the Smask object */
270     if (smask_objnum > 0) {
271         bitdepth = (int) png_get_bit_depth(png_ptr(img), png_info(img));
272         pdfbegindict(smask_objnum, 0);
273         pdf_puts("/Type /XObject\n/Subtype /Image\n");
274         pdf_printf("/Width %i\n/Height %i\n/BitsPerComponent %i\n",
275                    (int) png_get_image_width(png_ptr(img), png_info(img)),
276                    (int) png_get_image_height(png_ptr(img), png_info(img)),
277                    (bitdepth == 16 ? 8 : bitdepth));
278         pdf_puts("/ColorSpace /DeviceGray\n");
279         pdfbeginstream();
280         for (i = 0; i < smask_size; i++) {
281             if (i % 8 == 0)
282                 pdfroom(8);
283             pdfbuf[pdfptr++] = smask[i];
284             if (bitdepth == 16)
285                 i++;
286         }
287         xfree(smask);
288         pdfendstream();
289     }
290 }
291 
write_png_rgb(integer img)292 static void write_png_rgb(integer img)
293 {
294     int i, j, k, l;
295     png_bytep row, r, *rows;
296     if (img_colorspace_ref(img) != 0) {
297         pdf_printf("%i 0 R\n", (int) img_colorspace_ref(img));
298     } else {
299         pdf_puts("/DeviceRGB\n");
300     }
301     pdfbeginstream();
302     if (png_get_interlace_type(png_ptr(img), png_info(img)) == PNG_INTERLACE_NONE) {
303         row = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
304         write_noninterlaced(write_simple_pixel(r));
305         xfree(row);
306     } else {
307         if (png_get_image_height(png_ptr(img), png_info(img))
308             * png_get_rowbytes(png_ptr(img), png_info(img)) >= 10240000L)
309             pdftex_warn
310                 ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)");
311         rows = xtalloc(png_get_image_height(png_ptr(img), png_info(img)), png_bytep);
312         for (i = 0; (unsigned) i < png_get_image_height(png_ptr(img), png_info(img)); i++)
313             rows[i] = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
314         png_read_image(png_ptr(img), rows);
315         write_interlaced(write_simple_pixel(row));
316         xfree(rows);
317     }
318     pdfendstream();
319 }
320 
write_png_rgb_alpha(integer img)321 static void write_png_rgb_alpha(integer img)
322 {
323     int i, j, k, l;
324     png_bytep row, r, *rows;
325     integer smask_objnum = 0;
326     png_bytep smask;
327     integer smask_ptr = 0;
328     integer smask_size = 0;
329     int bitdepth;
330     if (img_colorspace_ref(img) != 0) {
331         pdf_printf("%i 0 R\n", (int) img_colorspace_ref(img));
332     } else {
333         pdf_puts("/DeviceRGB\n");
334     }
335     pdfcreateobj(0, 0);
336     smask_objnum = objptr;
337     pdf_printf("/SMask %i 0 R\n", (int) smask_objnum);
338     smask_size = (png_get_rowbytes(png_ptr(img), png_info(img)) / 2)
339                  * png_get_image_height(png_ptr(img), png_info(img));
340     smask = xtalloc(smask_size, png_byte);
341     pdfbeginstream();
342     if (png_get_interlace_type(png_ptr(img), png_info(img)) == PNG_INTERLACE_NONE) {
343         row = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
344         if ((png_get_bit_depth(png_ptr(img), png_info(img)) == 16) && fixedimagehicolor) {
345             write_noninterlaced(write_rgb_pixel_16(r));
346         } else {
347             write_noninterlaced(write_rgb_pixel_8(r));
348         }
349         xfree(row);
350     } else {
351         if (png_get_image_height(png_ptr(img), png_info(img))
352             * png_get_rowbytes(png_ptr(img), png_info(img)) >= 10240000L)
353             pdftex_warn
354                 ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)");
355         rows = xtalloc(png_get_image_height(png_ptr(img), png_info(img)), png_bytep);
356         for (i = 0; (unsigned) i < png_get_image_height(png_ptr(img), png_info(img)); i++)
357             rows[i] = xtalloc(png_get_rowbytes(png_ptr(img), png_info(img)), png_byte);
358         png_read_image(png_ptr(img), rows);
359         if ((png_get_bit_depth(png_ptr(img), png_info(img)) == 16) && fixedimagehicolor) {
360             write_interlaced(write_rgb_pixel_16(row));
361         } else {
362             write_interlaced(write_rgb_pixel_8(row));
363         }
364         xfree(rows);
365     }
366     pdfendstream();
367     pdfflush();
368     /* now write the Smask object */
369     if (smask_objnum > 0) {
370         bitdepth = (int) png_get_bit_depth(png_ptr(img), png_info(img));
371         pdfbegindict(smask_objnum, 0);
372         pdf_puts("/Type /XObject\n/Subtype /Image\n");
373         pdf_printf("/Width %i\n/Height %i\n/BitsPerComponent %i\n",
374                    (int) png_get_image_width(png_ptr(img), png_info(img)),
375                    (int) png_get_image_height(png_ptr(img), png_info(img)),
376                    (bitdepth == 16 ? 8 : bitdepth));
377         pdf_puts("/ColorSpace /DeviceGray\n");
378         pdfbeginstream();
379         for (i = 0; i < smask_size; i++) {
380             if (i % 8 == 0)
381                 pdfroom(8);
382             pdfbuf[pdfptr++] = smask[i];
383             if (bitdepth == 16)
384                 i++;
385         }
386         xfree(smask);
387         pdfendstream();
388     }
389 }
390 
391 
392 /**********************************************************************/
393 /*
394  *
395  * The |copy_png| function is from Hartmut Henkel. The goal is to use
396  * pdf's native FlateDecode support if that is possible.
397  *
398  * Only a subset of the png files allows this, but when possible it
399  * greatly improves inclusion speed.
400  *
401  */
402 
403 /* Code cheerfully gleaned from Thomas Merz' PDFlib, file p_png.c "SPNG - Simple PNG" */
404 
spng_getint(FILE * fp)405 static int spng_getint(FILE * fp)
406 {
407     unsigned char buf[4];
408     if (fread(buf, 1, 4, fp) != 4)
409         pdftex_fail("writepng: reading chunk type failed");
410     return ((((((int) buf[0] << 8) + buf[1]) << 8) + buf[2]) << 8) + buf[3];
411 }
412 
413 #define SPNG_CHUNK_IDAT 0x49444154
414 #define SPNG_CHUNK_IEND 0x49454E44
415 
copy_png(integer img)416 static void copy_png(integer img)
417 {
418     FILE *fp = (FILE *) png_get_io_ptr(png_ptr(img));
419     int i, len, type, streamlength = 0;
420     boolean endflag = false;
421     int idat = 0;               /* flag to check continuous IDAT chunks sequence */
422     /* 1st pass to find overall stream /Length */
423     if (fseek(fp, 8, SEEK_SET) != 0)
424         pdftex_fail("writepng: fseek in PNG file failed");
425     do {
426         len = spng_getint(fp);
427         type = spng_getint(fp);
428         switch (type) {
429         case SPNG_CHUNK_IEND:
430             endflag = true;
431             break;
432         case SPNG_CHUNK_IDAT:
433             streamlength += len;
434         default:
435             if (fseek(fp, len + 4, SEEK_CUR) != 0)
436                 pdftex_fail("writepng: fseek in PNG file failed");
437         }
438     } while (endflag == false);
439     pdf_printf("/Length %d\n"
440                "/Filter/FlateDecode\n"
441                "/DecodeParms<<"
442                "/Colors %d"
443                "/Columns %d"
444                "/BitsPerComponent %i"
445                "/Predictor 10>>\n>>\nstream\n", streamlength,
446                png_get_color_type(png_ptr(img), png_info(img)) == 2 ? 3 : 1,
447                (int) png_get_image_width(png_ptr(img), png_info(img)),
448                (int) png_get_bit_depth(png_ptr(img), png_info(img)));
449     /* 2nd pass to copy data */
450     endflag = false;
451     if (fseek(fp, 8, SEEK_SET) != 0)
452         pdftex_fail("writepng: fseek in PNG file failed");
453     do {
454         len = spng_getint(fp);
455         type = spng_getint(fp);
456         switch (type) {
457         case SPNG_CHUNK_IDAT:  /* do copy */
458             if (idat == 2)
459                 pdftex_fail("writepng: IDAT chunk sequence broken");
460             idat = 1;
461             while (len > 0) {
462                 i = (len > pdfbufsize) ? pdfbufsize : len;
463                 pdfroom(i);
464                 fread(&pdfbuf[pdfptr], 1, i, fp);
465                 pdfptr += i;
466                 len -= i;
467             }
468             if (fseek(fp, 4, SEEK_CUR) != 0)
469                 pdftex_fail("writepng: fseek in PNG file failed");
470             break;
471         case SPNG_CHUNK_IEND:  /* done */
472             pdfendstream();
473             endflag = true;
474             break;
475         default:
476             if (idat == 1)
477                 idat = 2;
478             if (fseek(fp, len + 4, SEEK_CUR) != 0)
479                 pdftex_fail("writepng: fseek in PNG file failed");
480         }
481     } while (endflag == false);
482 }
483 
484 static boolean last_png_needs_page_group;
485 static boolean transparent_page_group_was_written = false;
486 
487 /* Called after the xobject generated by write_png has been finished; used to
488  * write out additional objects */
write_additional_png_objects(void)489 static void write_additional_png_objects(void)
490 {
491     if (last_png_needs_page_group) {
492         if (!transparent_page_group_was_written && transparent_page_group > 0) {
493             // create new group object
494             transparent_page_group_was_written = true;
495             pdfbeginobj(transparent_page_group, 2);
496             if (getpdfcompresslevel() == 0) {
497                 pdf_puts("%PTEX Group needed for transparent pngs\n");
498             }
499             pdf_puts("<</Type/Group /S/Transparency /CS/DeviceRGB /I true>>\n");
500             pdfendobj();
501         }
502     }
503 }
504 
write_png(integer img)505 void write_png(integer img)
506 {
507 
508     boolean png_copy = true;
509     double gamma = 0.0;
510     png_fixed_point int_file_gamma = 0;
511 #ifndef PNG_FP_1
512    /* for libpng < 1.5.0 */
513 #define PNG_FP_1    100000
514 #endif
515     int i;
516     integer palette_objnum = 0;
517     png_colorp palette;
518     int num_palette;
519     last_png_needs_page_group = false;
520 
521     png_get_PLTE(png_ptr(img), png_info(img), &palette, &num_palette);
522 
523     if (fixedpdfminorversion < 5)
524         fixedimagehicolor = 0;
525 
526     pdf_puts("/Type /XObject\n/Subtype /Image\n");
527     /* simple transparency support */
528     if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_tRNS)) {
529         png_set_tRNS_to_alpha(png_ptr(img));
530         png_copy = false;
531     }
532     /* alpha channel support */
533     if (fixedpdfminorversion < 4
534         && png_get_color_type(png_ptr(img), png_info(img)) | PNG_COLOR_MASK_ALPHA) {
535         png_set_strip_alpha(png_ptr(img));
536         png_copy = false;
537     }
538     /* 16 bit depth support */
539     if (fixedpdfminorversion < 5)
540         fixedimagehicolor = 0;
541     if ((png_get_bit_depth(png_ptr(img), png_info(img)) == 16) && (fixedimagehicolor == 0)) {
542         png_set_strip_16(png_ptr(img));
543         png_copy = false;
544     }
545     /* gamma support */
546     if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_gAMA)) {
547         png_get_gAMA(png_ptr(img), png_info(img), &gamma);
548         png_get_gAMA_fixed(png_ptr(img), png_info(img), &int_file_gamma);
549     }
550     if (fixedimageapplygamma) {
551         if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_gAMA))
552             png_set_gamma(png_ptr(img), fixedgamma / 1000.0, gamma);
553         else
554             png_set_gamma(png_ptr(img), fixedgamma / 1000.0,
555                           1000.0 / fixedimagegamma);
556         png_copy = false;
557     }
558     /* reset structure */
559     (void) png_set_interlace_handling(png_ptr(img));
560     png_read_update_info(png_ptr(img), png_info(img));
561 
562     pdf_printf("/Width %i\n/Height %i\n/BitsPerComponent %i\n",
563                (int) png_get_image_width(png_ptr(img), png_info(img)),
564                (int) png_get_image_height(png_ptr(img), png_info(img)),
565                (int) png_get_bit_depth(png_ptr(img), png_info(img)));
566     pdf_puts("/ColorSpace ");
567     if (png_copy && fixedpdfminorversion > 1
568         && png_get_interlace_type(png_ptr(img), png_info(img)) == PNG_INTERLACE_NONE
569         && (png_get_color_type(png_ptr(img), png_info(img)) == PNG_COLOR_TYPE_GRAY
570             || png_get_color_type(png_ptr(img), png_info(img)) == PNG_COLOR_TYPE_RGB)
571         && !fixedimageapplygamma
572         && (!png_get_valid(png_ptr(img), png_info(img), PNG_INFO_gAMA)
573             || int_file_gamma == PNG_FP_1)
574         && !png_get_valid(png_ptr(img), png_info(img),
575                           PNG_INFO_cHRM | PNG_INFO_iCCP | PNG_INFO_sBIT | PNG_INFO_sRGB
576                           | PNG_INFO_bKGD | PNG_INFO_hIST | PNG_INFO_tRNS | PNG_INFO_sPLT)
577         ) {
578         /* Copy PNG */
579         if (img_colorspace_ref(img) != 0) {
580             pdf_printf("%i 0 R\n", (int) img_colorspace_ref(img));
581         } else {
582             switch (png_get_color_type(png_ptr(img), png_info(img))) {
583             case PNG_COLOR_TYPE_PALETTE:
584                 pdfcreateobj(0, 0);
585                 palette_objnum = objptr;
586                 pdf_printf("[/Indexed /DeviceRGB %i %i 0 R]\n",
587                            num_palette - 1, (int) palette_objnum);
588                 break;
589             case PNG_COLOR_TYPE_GRAY:
590                 pdf_puts("/DeviceGray\n");
591                 break;
592             default:           /* RGB */
593                 pdf_puts("/DeviceRGB\n");
594             };
595         }
596         tex_printf(" (PNG copy)");
597         copy_png(img);
598         if (palette_objnum > 0) {
599             pdfbegindict(palette_objnum, 0);
600             pdfbeginstream();
601             for (i = 0; i < num_palette; i++) {
602                 pdfroom(3);
603                 pdfbuf[pdfptr++] = palette[i].red;
604                 pdfbuf[pdfptr++] = palette[i].green;
605                 pdfbuf[pdfptr++] = palette[i].blue;
606             }
607             pdfendstream();
608         }
609     } else {
610         if (0) {
611             tex_printf(" *** PNG copy skipped because:");
612             if (!png_copy)
613                 tex_printf(" !png_copy");
614             if (fixedpdfminorversion <= 1)
615                 tex_printf(" minorversion=%d", (int) fixedpdfminorversion);
616             if (png_get_interlace_type(png_ptr(img), png_info(img)) != PNG_INTERLACE_NONE)
617                 tex_printf(" interlaced");
618             if (!((png_get_color_type(png_ptr(img), png_info(img)) == PNG_COLOR_TYPE_GRAY)
619                   || (png_get_color_type(png_ptr(img), png_info(img)) == PNG_COLOR_TYPE_RGB)))
620                 tex_printf(" colortype");
621             if (fixedimageapplygamma)
622                 tex_printf(" apply gamma");
623             if (!(!png_get_valid(png_ptr(img), png_info(img), PNG_INFO_gAMA)
624                   || int_file_gamma == PNG_FP_1))
625                 tex_printf(" gamma");
626             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_cHRM))
627                 tex_printf(" cHRM");
628             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_iCCP))
629                 tex_printf(" iCCP");
630             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_sBIT))
631                 tex_printf(" sBIT");
632             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_sRGB))
633                 tex_printf(" sRGB");
634             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_bKGD))
635                 tex_printf(" bKGD");
636             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_hIST))
637                 tex_printf(" hIST");
638             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_tRNS))
639                 tex_printf(" tRNS");
640             if (png_get_valid(png_ptr(img), png_info(img), PNG_INFO_sPLT))
641                 tex_printf(" sPLT");
642         }
643         switch (png_get_color_type(png_ptr(img), png_info(img))) {
644         case PNG_COLOR_TYPE_PALETTE:
645             write_png_palette(img);
646             break;
647         case PNG_COLOR_TYPE_GRAY:
648             write_png_gray(img);
649             break;
650         case PNG_COLOR_TYPE_GRAY_ALPHA:
651             if (fixedpdfminorversion >= 4) {
652                 write_png_gray_alpha(img);
653                 last_png_needs_page_group = true;
654             } else
655                 write_png_gray(img);
656             break;
657         case PNG_COLOR_TYPE_RGB:
658             write_png_rgb(img);
659             break;
660         case PNG_COLOR_TYPE_RGB_ALPHA:
661             if (fixedpdfminorversion >= 4) {
662                 write_png_rgb_alpha(img);
663                 last_png_needs_page_group = true;
664             } else
665                 write_png_rgb(img);
666             break;
667         default:
668             pdftex_fail("unsupported type of color_type <%i>",
669                         png_get_color_type(png_ptr(img), png_info(img)));
670         }
671     }
672     pdfflush();
673     write_additional_png_objects();
674 }
675