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