Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpspng.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* XPS interpreter - PNG image support */
18
19
#include "ghostxps.h"
20
21
#include "stream.h"
22
#include "strimpl.h"
23
#include "gsstate.h"
24
#include "setjmp_.h"
25
26
/* silence a warning where #if SHARE_LIBPNG is used when it's undefined */
27
#ifndef SHARE_LIBPNG
28
#define SHARE_LIBPNG 0
29
#endif
30
#include "png_.h"
31
32
/*
33
 * PNG using libpng directly (no gs wrappers)
34
 */
35
36
struct xps_png_io_s
37
{
38
    byte *ptr;
39
    byte *lim;
40
};
41
42
static void
43
xps_png_read(png_structp png, png_bytep data, png_size_t length)
44
0
{
45
0
    struct xps_png_io_s *io = png_get_io_ptr(png);
46
0
    if (io->ptr + length > io->lim)
47
0
        png_error(png, "Read Error");
48
0
    memcpy(data, io->ptr, length);
49
0
    io->ptr += length;
50
0
}
51
52
0
#define PNG_MEM_ALIGN 16
53
static png_voidp
54
xps_png_malloc(png_structp png, png_size_t size)
55
0
{
56
0
    gs_memory_t *mem = png_get_mem_ptr(png);
57
0
    uchar *unaligned;
58
0
    uchar *aligned;
59
60
0
    if (size == 0)
61
0
        return NULL;
62
0
    unaligned = gs_alloc_bytes(mem, size + PNG_MEM_ALIGN, "libpng");
63
0
    if (unaligned == NULL)
64
0
        return NULL;
65
66
0
    aligned = (uchar *)((intptr_t)(unaligned + PNG_MEM_ALIGN) & ~(PNG_MEM_ALIGN - 1));
67
0
    aligned[-1] = (uchar)(aligned - unaligned);
68
69
0
    return aligned;
70
0
}
71
72
static void
73
xps_png_free(png_structp png, png_voidp ptr)
74
0
{
75
0
    gs_memory_t *mem = png_get_mem_ptr(png);
76
0
    uchar *aligned = ptr;
77
0
    if (aligned == NULL)
78
0
        return;
79
0
    gs_free_object(mem, aligned - aligned[-1], "libpng");
80
0
}
81
82
/* This only determines if we have an alpha value */
83
OPTIMIZE_SETJMP
84
int
85
xps_png_has_alpha(xps_context_t *ctx, byte *rbuf, int rlen)
86
0
{
87
0
    png_structp png;
88
0
    png_infop info;
89
0
    struct xps_png_io_s io;
90
0
    int has_alpha;
91
92
    /*
93
     * Set up PNG structs and input source
94
     */
95
96
0
    io.ptr = rbuf;
97
0
    io.lim = rbuf + rlen;
98
99
0
    png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
100
0
            NULL, NULL, NULL,
101
0
            ctx->memory, xps_png_malloc, xps_png_free);
102
0
    if (!png) {
103
0
        gs_warn("png_create_read_struct");
104
0
        return 0;
105
0
    }
106
107
0
    info = png_create_info_struct(png);
108
0
    if (!info) {
109
0
        gs_warn("png_create_info_struct");
110
0
        return 0;
111
0
    }
112
113
0
    png_set_read_fn(png, &io, xps_png_read);
114
0
    png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE);
115
116
    /*
117
     * Jump to here on errors.
118
     */
119
120
0
    if (setjmp(png_jmpbuf(png)))
121
0
    {
122
0
        png_destroy_read_struct(&png, &info, NULL);
123
0
        gs_warn("png reading failed");
124
0
        return 0;
125
0
    }
126
127
    /*
128
     * Read PNG header
129
     */
130
0
    png_read_info(png, info);
131
0
    if (png_get_valid(png, info, PNG_INFO_tRNS))
132
0
    {
133
        /* this will also expand the depth to 8-bits */
134
0
        png_set_tRNS_to_alpha(png);
135
0
    }
136
0
    png_read_update_info(png, info);
137
138
0
    switch (png_get_color_type(png, info))
139
0
    {
140
0
    case PNG_COLOR_TYPE_PALETTE:
141
0
    case PNG_COLOR_TYPE_GRAY:
142
0
    case PNG_COLOR_TYPE_RGB:
143
0
        has_alpha = 0;
144
0
        break;
145
146
0
    case PNG_COLOR_TYPE_GRAY_ALPHA:
147
0
    case PNG_COLOR_TYPE_RGB_ALPHA:
148
0
        has_alpha = 1;
149
0
        break;
150
151
0
    default:
152
0
        gs_warn("cannot handle this png color type");
153
0
        has_alpha = 0;
154
0
        break;
155
0
    }
156
157
    /*
158
     * Clean up memory.
159
     */
160
161
0
    png_destroy_read_struct(&png, &info, NULL);
162
163
0
    return has_alpha;
164
0
}
165
166
OPTIMIZE_SETJMP
167
int
168
xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image)
169
0
{
170
0
    png_structp png;
171
0
    png_infop info;
172
0
    struct xps_png_io_s io;
173
0
    int npasses;
174
0
    int pass;
175
0
    int y;
176
0
    int compression = 0; /* dummy as of libpng 1.5.x */
177
0
    char *iccname = NULL;
178
0
    char *iccprofile = NULL;
179
0
    unsigned int iccproflen = 0;
180
0
    png_uint_32 xres, yres;
181
0
    int unit;
182
183
    /*
184
     * Set up PNG structs and input source
185
     */
186
187
0
    io.ptr = rbuf;
188
0
    io.lim = rbuf + rlen;
189
190
0
    png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
191
0
            NULL, NULL, NULL,
192
0
            ctx->memory, xps_png_malloc, xps_png_free);
193
0
    if (!png)
194
0
        return gs_throw(-1, "png_create_read_struct");
195
196
0
    info = png_create_info_struct(png);
197
0
    if (!info)
198
0
        return gs_throw(-1, "png_create_info_struct");
199
200
0
    png_set_read_fn(png, &io, xps_png_read);
201
0
    png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE);
202
203
    /*
204
     * Jump to here on errors.
205
     */
206
207
0
    if (setjmp(png_jmpbuf(png)))
208
0
    {
209
0
        png_destroy_read_struct(&png, &info, NULL);
210
0
        return gs_throw(-1, "png reading failed");
211
0
    }
212
213
    /*
214
     * Read PNG header
215
     */
216
217
0
    png_read_info(png, info);
218
219
0
    if (png_get_interlace_type(png, info) == PNG_INTERLACE_ADAM7)
220
0
    {
221
0
        npasses = png_set_interlace_handling(png);
222
0
    }
223
0
    else
224
0
    {
225
0
        npasses = 1;
226
0
    }
227
228
0
    if (png_get_color_type(png, info) == PNG_COLOR_TYPE_PALETTE)
229
0
    {
230
0
        png_set_palette_to_rgb(png);
231
0
    }
232
233
0
    if (png_get_valid(png, info, PNG_INFO_tRNS))
234
0
    {
235
        /* this will also expand the depth to 8-bits */
236
0
        png_set_tRNS_to_alpha(png);
237
0
    }
238
239
0
    png_read_update_info(png, info);
240
241
0
    image->width = png_get_image_width(png, info);
242
0
    image->height = png_get_image_height(png, info);
243
0
    image->comps = png_get_channels(png, info);
244
0
    image->bits = png_get_bit_depth(png, info);
245
0
    image->invert_decode = false;
246
0
    if (image->width <= 0 || image->height <= 0 || image->comps <= 0 || image->bits <= 0)
247
0
        return gs_throw(-1, "bad image dimension");
248
249
    /* See if we have an icc profile */
250
0
#if PNG_LIBPNG_VER_MINOR >= 5
251
    /* ignore the return value */
252
0
    (void)png_get_iCCP(png, info, (png_charpp)&iccname, &compression, (png_bytepp)&iccprofile, (png_uint_32 *)&iccproflen);
253
#else
254
    iccprofile = info->iccp_profile;
255
    iccproflen = info->iccp_proflen;
256
#endif
257
0
    if (iccprofile != NULL)
258
0
    {
259
0
        image->profilesize = iccproflen;
260
0
        image->profile = xps_alloc(ctx, iccproflen);
261
0
        if (image->profile)
262
0
        {
263
            /* If we can't create it, just ignore */
264
0
            memcpy(image->profile, iccprofile, iccproflen);
265
0
        }
266
0
    }
267
268
0
    switch (png_get_color_type(png, info))
269
0
    {
270
0
    case PNG_COLOR_TYPE_GRAY:
271
0
        rc_increment(ctx->gray);
272
0
        image->colorspace = ctx->gray;
273
0
        image->hasalpha = 0;
274
0
        break;
275
276
0
    case PNG_COLOR_TYPE_RGB:
277
0
        rc_increment(ctx->srgb);
278
0
        image->colorspace = ctx->srgb;
279
0
        image->hasalpha = 0;
280
0
        break;
281
282
0
    case PNG_COLOR_TYPE_GRAY_ALPHA:
283
0
        rc_increment(ctx->gray);
284
0
        image->colorspace = ctx->gray;
285
0
        image->hasalpha = 1;
286
0
        break;
287
288
0
    case PNG_COLOR_TYPE_RGB_ALPHA:
289
0
        rc_increment(ctx->srgb);
290
0
        image->colorspace = ctx->srgb;
291
0
        image->hasalpha = 1;
292
0
        break;
293
294
0
    default:
295
0
        return gs_throw(-1, "cannot handle this png color type");
296
0
    }
297
298
    /*
299
     * Extract DPI, default to 96 dpi
300
     */
301
302
0
    image->xres = 96;
303
0
    image->yres = 96;
304
305
0
    if (png_get_pHYs(png, info, &xres, &yres, &unit) & PNG_INFO_pHYs)
306
0
    {
307
0
        if (unit == PNG_RESOLUTION_METER)
308
0
        {
309
0
            image->xres = (int)(xres * 0.0254 + 0.5);
310
0
            image->yres = (int)(yres * 0.0254 + 0.5);
311
0
        }
312
0
    }
313
314
    /*
315
     * Read rows, filling transformed output into image buffer.
316
     */
317
318
0
    image->stride = (image->width * image->comps * image->bits + 7) / 8;
319
320
0
    image->samples = xps_alloc(ctx, (size_t)image->stride * image->height);
321
0
    if (!image->samples) {
322
0
        return gs_throw(gs_error_VMerror, "out of memory.\n");
323
0
    }
324
325
0
    for (pass = 0; pass < npasses; pass++)
326
0
    {
327
0
        for (y = 0; y < image->height; y++)
328
0
        {
329
0
            png_read_row(png, image->samples + (y * image->stride), NULL);
330
0
        }
331
0
    }
332
333
    /*
334
     * Clean up memory.
335
     */
336
337
0
    png_destroy_read_struct(&png, &info, NULL);
338
339
0
    return gs_okay;
340
0
}