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