Coverage Report

Created: 2026-04-09 07:06

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
0
    uint32_t size;
183
184
    /*
185
     * Set up PNG structs and input source
186
     */
187
188
0
    io.ptr = rbuf;
189
0
    io.lim = rbuf + rlen;
190
191
0
    png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
192
0
            NULL, NULL, NULL,
193
0
            ctx->memory, xps_png_malloc, xps_png_free);
194
0
    if (!png)
195
0
        return gs_throw(-1, "png_create_read_struct");
196
197
0
    info = png_create_info_struct(png);
198
0
    if (!info)
199
0
        return gs_throw(-1, "png_create_info_struct");
200
201
0
    png_set_read_fn(png, &io, xps_png_read);
202
0
    png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE);
203
204
    /*
205
     * Jump to here on errors.
206
     */
207
208
0
    if (setjmp(png_jmpbuf(png)))
209
0
    {
210
0
        png_destroy_read_struct(&png, &info, NULL);
211
0
        return gs_throw(-1, "png reading failed");
212
0
    }
213
214
    /*
215
     * Read PNG header
216
     */
217
218
0
    png_read_info(png, info);
219
220
0
    if (png_get_interlace_type(png, info) == PNG_INTERLACE_ADAM7)
221
0
    {
222
0
        npasses = png_set_interlace_handling(png);
223
0
    }
224
0
    else
225
0
    {
226
0
        npasses = 1;
227
0
    }
228
229
0
    if (png_get_color_type(png, info) == PNG_COLOR_TYPE_PALETTE)
230
0
    {
231
0
        png_set_palette_to_rgb(png);
232
0
    }
233
234
0
    if (png_get_valid(png, info, PNG_INFO_tRNS))
235
0
    {
236
        /* this will also expand the depth to 8-bits */
237
0
        png_set_tRNS_to_alpha(png);
238
0
    }
239
240
0
    png_read_update_info(png, info);
241
242
0
    image->width = png_get_image_width(png, info);
243
0
    image->height = png_get_image_height(png, info);
244
0
    image->comps = png_get_channels(png, info);
245
0
    image->bits = png_get_bit_depth(png, info);
246
0
    image->invert_decode = false;
247
0
    if (image->width <= 0 || image->height <= 0 || image->comps <= 0 || image->bits <= 0)
248
0
        return gs_throw(-1, "bad image dimension");
249
250
    /* See if we have an icc profile */
251
0
#if PNG_LIBPNG_VER_MINOR >= 5
252
    /* ignore the return value */
253
0
    (void)png_get_iCCP(png, info, (png_charpp)&iccname, &compression, (png_bytepp)&iccprofile, (png_uint_32 *)&iccproflen);
254
#else
255
    iccprofile = info->iccp_profile;
256
    iccproflen = info->iccp_proflen;
257
#endif
258
0
    if (iccprofile != NULL)
259
0
    {
260
0
        image->profilesize = iccproflen;
261
0
        image->profile = xps_alloc(ctx, iccproflen);
262
0
        if (image->profile)
263
0
        {
264
            /* If we can't create it, just ignore */
265
0
            memcpy(image->profile, iccprofile, iccproflen);
266
0
        }
267
0
    }
268
269
0
    switch (png_get_color_type(png, info))
270
0
    {
271
0
    case PNG_COLOR_TYPE_GRAY:
272
0
        rc_increment(ctx->gray);
273
0
        image->colorspace = ctx->gray;
274
0
        image->hasalpha = 0;
275
0
        break;
276
277
0
    case PNG_COLOR_TYPE_RGB:
278
0
        rc_increment(ctx->srgb);
279
0
        image->colorspace = ctx->srgb;
280
0
        image->hasalpha = 0;
281
0
        break;
282
283
0
    case PNG_COLOR_TYPE_GRAY_ALPHA:
284
0
        rc_increment(ctx->gray);
285
0
        image->colorspace = ctx->gray;
286
0
        image->hasalpha = 1;
287
0
        break;
288
289
0
    case PNG_COLOR_TYPE_RGB_ALPHA:
290
0
        rc_increment(ctx->srgb);
291
0
        image->colorspace = ctx->srgb;
292
0
        image->hasalpha = 1;
293
0
        break;
294
295
0
    default:
296
0
        return gs_throw(-1, "cannot handle this png color type");
297
0
    }
298
299
    /*
300
     * Extract DPI, default to 96 dpi
301
     */
302
303
0
    image->xres = 96;
304
0
    image->yres = 96;
305
306
0
    if (png_get_pHYs(png, info, &xres, &yres, &unit) & PNG_INFO_pHYs)
307
0
    {
308
0
        if (unit == PNG_RESOLUTION_METER)
309
0
        {
310
0
            image->xres = (int)(xres * 0.0254 + 0.5);
311
0
            image->yres = (int)(yres * 0.0254 + 0.5);
312
0
        }
313
0
    }
314
315
    /*
316
     * Read rows, filling transformed output into image buffer.
317
     */
318
319
0
    if (check_uint32_multiply((uint32_t)image->width, ((uint32_t)image->bits), &size) != 0)
320
0
        return gs_throw(-1, "image  is too large");
321
322
0
    if (size > INT_MAX / image->comps)
323
0
        return gs_throw(-1, "image  is too large");
324
325
0
    image->stride = (size * image->comps + 7) / 8;
326
327
0
    if (check_uint32_multiply((uint32_t)image->stride, (uint32_t)image->height, &size) != 0)
328
0
        return gs_throw(-1, "image  is too large");
329
330
0
    image->samples = xps_alloc(ctx, size);
331
0
    if (!image->samples)
332
0
        return gs_throw(gs_error_VMerror, "out of memory.\n");
333
334
0
    for (pass = 0; pass < npasses; pass++)
335
0
    {
336
0
        for (y = 0; y < image->height; y++)
337
0
        {
338
0
            png_read_row(png, image->samples + (y * image->stride), NULL);
339
0
        }
340
0
    }
341
342
    /*
343
     * Clean up memory.
344
     */
345
346
0
    png_destroy_read_struct(&png, &info, NULL);
347
348
0
    return gs_okay;
349
0
}