Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/gpdl/pngtop.c
Line
Count
Source
1
/* Copyright (C) 2019-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
/* pngtop.c */
17
/* Top-level API implementation of "PNG" Language Interface */
18
19
#include "pltop.h"
20
#include "gserrors.h"
21
#include "gxdevice.h"
22
#include "gsstate.h"
23
#include "strimpl.h"
24
#include "gscoord.h"
25
#include "gsicc_manage.h"
26
#include "gspaint.h"
27
#include "plmain.h"
28
#include "png_.h"
29
#include "setjmp_.h"
30
31
/* Forward decls */
32
33
/************************************************************/
34
/******** Language wrapper implementation (see pltop.h) *****/
35
/************************************************************/
36
37
typedef enum
38
{
39
    ii_state_identifying = 0,
40
    ii_state_png,
41
    ii_state_png_decode,
42
    ii_state_flush
43
} ii_state;
44
45
/*
46
 * Png interpreter instance
47
 */
48
typedef struct png_interp_instance_s {
49
    gs_memory_t       *memory;
50
    gs_memory_t       *cmemory;
51
    gx_device         *dev;
52
    gx_device         *nulldev;
53
54
    gs_color_space    *gray;
55
    gs_color_space    *rgb;
56
57
    /* Png parser state machine */
58
    ii_state           state;
59
60
    int                pages;
61
62
    uint8_t            bpp;
63
    uint8_t            cs;
64
    uint32_t           width;
65
    uint32_t           height;
66
    uint32_t           xresolution;
67
    uint32_t           yresolution;
68
    int                interlaced;
69
70
    uint32_t           num_comps;
71
    uint32_t           byte_width;
72
    uint32_t           y;
73
    uint32_t           passes;
74
75
    uint32_t           bytes_available_on_entry;
76
77
    gs_image_t         image;
78
    gs_image_enum     *penum;
79
    gs_gstate         *pgs;
80
81
    png_structp        png;
82
    png_infop          png_info;
83
    size_t             buffer_full;
84
    size_t             buffer_max;
85
    byte              *buffer;
86
    size_t             file_pos;
87
88
    byte              *samples;
89
90
} png_interp_instance_t;
91
92
static int
93
png_detect_language(const char *s, int len)
94
28.0k
{
95
28.0k
    const byte *hdr = (const byte *)s;
96
28.0k
    if (len >= 8) {
97
27.5k
        if (hdr[0] == 137 &&
98
330
            hdr[1] == 80 &&
99
323
            hdr[2] == 78 &&
100
320
            hdr[3] == 71 &&
101
319
            hdr[4] == 13 &&
102
318
            hdr[5] == 10 &&
103
317
            hdr[6] == 26 &&
104
316
            hdr[7] == 10)
105
315
            return 100;
106
27.5k
    }
107
108
27.7k
    return 0;
109
28.0k
}
110
111
static const pl_interp_characteristics_t png_characteristics = {
112
    "PNG",
113
    png_detect_language,
114
};
115
116
/* Get implementation's characteristics */
117
static const pl_interp_characteristics_t * /* always returns a descriptor */
118
png_impl_characteristics(const pl_interp_implementation_t *impl)     /* implementation of interpreter to alloc */
119
59.1k
{
120
59.1k
  return &png_characteristics;
121
59.1k
}
122
123
static void
124
png_deallocate(png_interp_instance_t *png)
125
16.1k
{
126
16.1k
    if (png == NULL)
127
0
        return;
128
129
16.1k
    rc_decrement_cs(png->gray, "png_deallocate");
130
16.1k
    rc_decrement_cs(png->rgb, "png_deallocate");
131
132
16.1k
    if (png->pgs != NULL)
133
16.1k
        gs_gstate_free_chain(png->pgs);
134
16.1k
    gs_free_object(png->memory, png, "png_impl_allocate_interp_instance");
135
16.1k
}
136
137
/* Deallocate a interpreter instance */
138
static int
139
png_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
140
16.1k
{
141
16.1k
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
142
143
16.1k
    png_deallocate(png);
144
16.1k
    impl->interp_client_data = NULL;
145
146
16.1k
    return 0;
147
16.1k
}
148
149
/* Do per-instance interpreter allocation/init. */
150
static int
151
png_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
152
16.1k
{
153
16.1k
    int code;
154
16.1k
    png_interp_instance_t *png
155
16.1k
        = (png_interp_instance_t *)gs_alloc_bytes(mem,
156
16.1k
                                                  sizeof(png_interp_instance_t),
157
16.1k
                                                  "png_impl_allocate_interp_instance");
158
16.1k
    if (!png)
159
0
        return_error(gs_error_VMerror);
160
16.1k
    memset(png, 0, sizeof(*png));
161
162
16.1k
    png->memory = mem;
163
16.1k
    png->pgs = gs_gstate_alloc(mem);
164
16.1k
    if (png->pgs == NULL)
165
0
        goto failVM;
166
167
    /* Push one save level onto the stack to assuage the memory handling */
168
16.1k
    code = gs_gsave(png->pgs);
169
16.1k
    if (code < 0)
170
0
        goto fail;
171
172
16.1k
    code = gsicc_init_iccmanager(png->pgs);
173
16.1k
    if (code < 0)
174
0
        goto fail;
175
176
16.1k
    png->gray = gs_cspace_new_ICC(mem, png->pgs, 1);
177
16.1k
    png->rgb  = gs_cspace_new_ICC(mem, png->pgs, 3);
178
179
16.1k
    impl->interp_client_data = png;
180
181
16.1k
    return 0;
182
183
0
failVM:
184
0
    code = gs_note_error(gs_error_VMerror);
185
0
fail:
186
0
    (void)png_deallocate(png);
187
0
    return code;
188
0
}
189
190
/*
191
 * Get the allocator with which to allocate a device
192
 */
193
static gs_memory_t *
194
png_impl_get_device_memory(pl_interp_implementation_t *impl)
195
0
{
196
0
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
197
198
0
    return png->dev ? png->dev->memory : NULL;
199
0
}
200
201
/* Prepare interp instance for the next "job" */
202
static int
203
png_impl_init_job(pl_interp_implementation_t *impl,
204
                  gx_device                  *device)
205
308
{
206
308
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
207
208
308
    png->dev = device;
209
308
    png->state = ii_state_identifying;
210
211
308
    return 0;
212
308
}
213
214
/* Do any setup for parser per-cursor */
215
static int                      /* ret 0 or +ve if ok, else -ve error code */
216
png_impl_process_begin(pl_interp_implementation_t * impl)
217
308
{
218
308
    return 0;
219
308
}
220
221
/* Ensure we have 'required' bytes to read, and further ensure
222
 * that we have no UEL's within those bytes. */
223
static int
224
ensure_bytes(png_interp_instance_t *png, stream_cursor_read *pr, int required)
225
308
{
226
308
    int n;
227
308
    const uint8_t *p = pr->ptr+1;
228
308
    const uint8_t *q;
229
308
    int avail;
230
231
    /* Find out how many bytes we need to check */
232
308
    n = pr->limit - pr->ptr;
233
308
    if (n > required)
234
307
        n = required;
235
236
    /* Make sure there are no UELs in that block */
237
308
    q = p + n;
238
308
    while (p != q) {
239
2.77k
        while (p != q && *p != '\033')
240
2.46k
            p++;
241
308
        if (p == q)
242
308
            break;
243
0
        avail = pr->limit - pr->ptr;
244
0
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
245
            /* At least a partial match to a UEL */
246
0
            return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit;
247
0
        }
248
0
        p++;
249
0
    }
250
251
    /* If we have enough bytes, great, if not, get some more */
252
308
    return (n < required) ? gs_error_NeedInput : 0;
253
308
}
254
255
static int
256
flush_to_uel(stream_cursor_read *pr)
257
308
{
258
308
    const uint8_t *p = pr->ptr+1;
259
308
    const uint8_t *q = pr->limit+1;
260
308
    int avail;
261
262
308
    while (p != q) {
263
0
        while (p != q && *p != '\033')
264
0
            p++;
265
0
        if (p == q)
266
0
            break;
267
0
        avail = pr->limit - pr->ptr;
268
0
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
269
            /* At least a partial match to a UEL. Bin everything to
270
             * the start of the match. */
271
0
            pr->ptr = p-1;
272
0
            if (avail == 9) /* Complete match. Exit! */
273
0
                return gs_error_InterpreterExit;
274
            /* Partial match. Get more data. */
275
0
            return gs_error_NeedInput;
276
0
        }
277
0
        p++;
278
0
    }
279
280
308
    pr->ptr = pr->limit;
281
282
308
    return 0;
283
308
}
284
285
static int
286
bytes_until_uel(const stream_cursor_read *pr)
287
2.30k
{
288
2.30k
    const uint8_t *p = pr->ptr+1;
289
2.30k
    const uint8_t *q = pr->limit+1;
290
2.30k
    int avail;
291
292
6.38k
    while (p != q) {
293
1.57M
        while (p != q && *p != '\033')
294
1.56M
            p++;
295
5.05k
        if (p == q)
296
960
            break;
297
4.09k
        avail = q - p;
298
4.09k
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
299
            /* At least a partial match to a UEL. Everything up to
300
             * the start of the match is up for grabs. */
301
15
            return p - (pr->ptr+1);
302
15
        }
303
4.08k
        p++;
304
4.08k
    }
305
306
2.28k
    return pr->limit - pr->ptr;
307
2.30k
}
308
309
OPTIMIZE_SETJMP
310
static void
311
my_png_error(png_structp png_ptr, png_const_charp error_msg)
312
298
{
313
    /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */
314
315
298
    png_longjmp(png_ptr, 1);
316
298
}
317
318
static void
319
my_png_warning(png_structp png_ptr, png_const_charp warning_msg)
320
19.8k
{
321
    /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */
322
19.8k
}
323
324
static png_voidp
325
my_png_malloc(png_structp png_ptr, png_alloc_size_t size)
326
7.22k
{
327
7.22k
    png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr);
328
329
7.22k
    if (sizeof(void *) == 8) {
330
        /* gs_alloc_bytes returns blocks aligned to 8 on 64bit platforms.
331
         * PNG (on Windows at least) requires blocks aligned to 16. */
332
7.22k
        unsigned char *block = gs_alloc_bytes(png->memory, size+16, "my_png_malloc");
333
7.22k
        intptr_t num_bytes_padded;
334
335
7.22k
        if (block == NULL)
336
0
            return NULL;
337
7.22k
        num_bytes_padded = 16-(((intptr_t)block) & 15);
338
7.22k
        block += num_bytes_padded;
339
7.22k
        block[-1] = num_bytes_padded;
340
341
7.22k
        return block;
342
7.22k
    }
343
0
    return gs_alloc_bytes(png->memory, size, "my_png_malloc");
344
7.22k
}
345
346
static void
347
my_png_free(png_structp png_ptr, png_voidp ptr)
348
7.22k
{
349
7.22k
    png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr);
350
351
7.22k
    if (sizeof(void *) == 8) {
352
7.22k
        unsigned char *block = ptr;
353
7.22k
        if (block == NULL)
354
0
            return;
355
7.22k
        block -= block[-1];
356
7.22k
        ptr = (void *)block;
357
7.22k
    }
358
7.22k
    gs_free_object(png->memory, ptr, "my_png_free");
359
7.22k
}
360
361
static void
362
my_png_read(png_structp png_ptr, png_bytep data, png_size_t length)
363
58.5k
{
364
58.5k
    png_interp_instance_t *png = (png_interp_instance_t *)png_get_io_ptr(png_ptr);
365
58.5k
    if (length + png->file_pos > png->buffer_full)
366
218
        png_error(png_ptr, "Overread!");
367
368
58.3k
    memcpy(data, &png->buffer[png->file_pos], length);
369
58.3k
    png->file_pos += length;
370
58.3k
}
371
372
OPTIMIZE_SETJMP
373
static int
374
do_impl_process(png_interp_instance_t *png, stream_cursor_read * pr, bool eof)
375
1.33k
{
376
1.33k
    int code = 0, bytes;
377
1.33k
    ii_state ostate;
378
1.33k
    size_t bytes_in;
379
1.33k
    int advanced;
380
381
    /* Loop until we stop 'advancing'. */
382
1.33k
    do
383
3.22k
    {
384
3.22k
        ostate = png->state;
385
3.22k
        bytes_in = pr->limit - pr->ptr;
386
3.22k
        advanced = 0;
387
3.22k
        switch(png->state)
388
3.22k
        {
389
308
        case ii_state_identifying:
390
308
        {
391
308
            const byte *hdr;
392
            /* Try and get us 8 bytes */
393
308
            code = ensure_bytes(png, pr, 8);
394
308
            if (code < 0)
395
0
                return code;
396
308
            hdr = pr->ptr+1;
397
308
            if (hdr[0] == 137 &&
398
308
                hdr[1] == 80 &&
399
308
                hdr[2] == 78 &&
400
308
                hdr[3] == 71 &&
401
308
                hdr[4] == 13 &&
402
308
                hdr[5] == 10 &&
403
308
                hdr[6] == 26 &&
404
308
                hdr[7] == 10) {
405
308
                png->state = ii_state_png;
406
308
                break;
407
308
            }
408
0
            png->state = ii_state_flush;
409
0
            break;
410
308
        }
411
2.30k
        case ii_state_png:
412
2.30k
        {
413
            /* Gather data into a buffer */
414
2.30k
            bytes = bytes_until_uel(pr);
415
416
2.30k
            if (bytes == 0 && pr->limit - pr->ptr > 9) {
417
                /* No bytes until UEL, and there is space for a UEL in the buffer */
418
0
                png->state = ii_state_png_decode;
419
0
                png->file_pos = 0;
420
0
                break;
421
0
            }
422
2.30k
            if (bytes == 0 && eof) {
423
                /* No bytes until UEL, and we are at eof */
424
308
                png->state = ii_state_png_decode;
425
308
                png->file_pos = 0;
426
308
                break;
427
308
            }
428
429
1.99k
            if (png->buffer_full + bytes > png->buffer_max) {
430
                /* Need to expand our buffer */
431
320
                size_t proposed = png->buffer_full*2;
432
320
                if (proposed == 0)
433
308
                    proposed = 32768;
434
320
                while (proposed < png->buffer_full + bytes)
435
0
                    proposed *= 2;
436
437
320
                if (png->buffer == NULL) {
438
308
                    png->buffer = gs_alloc_bytes(png->memory, proposed, "png_buffer");
439
308
                    if (png->buffer == NULL) {
440
0
                        png->state = ii_state_flush;
441
0
                        break;
442
0
                    }
443
308
                } else {
444
12
                    void *new_buf = gs_resize_object(png->memory, png->buffer, proposed, "png_buffer");
445
12
                    if (new_buf == NULL) {
446
0
                        png->state = ii_state_flush;
447
0
                        break;
448
0
                    }
449
12
                    png->buffer = new_buf;
450
12
                }
451
320
                png->buffer_max = proposed;
452
320
            }
453
454
1.99k
            memcpy(&png->buffer[png->buffer_full], pr->ptr+1, bytes);
455
1.99k
            png->buffer_full += bytes;
456
1.99k
            pr->ptr += bytes;
457
1.99k
            break;
458
1.99k
        }
459
308
        case ii_state_png_decode:
460
308
        {
461
308
            gs_color_space *cs;
462
308
            float scale, xext, yext, xoffset, yoffset;
463
464
308
            png->png = png_create_read_struct_2(
465
308
                               PNG_LIBPNG_VER_STRING,
466
308
                               (png_voidp)png,
467
308
                               my_png_error,
468
308
                               my_png_warning,
469
308
                               (png_voidp)png,
470
308
                               my_png_malloc,
471
308
                               my_png_free);
472
308
            if (png->png == NULL) {
473
0
                png->state = ii_state_flush;
474
0
                break;
475
0
            }
476
477
308
            png->png_info = png_create_info_struct(png->png);
478
308
            if (!png->png_info) {
479
0
                png->state = ii_state_flush;
480
0
                break;
481
0
            }
482
483
308
            png_set_read_fn(png->png, png, my_png_read);
484
308
            png_set_alpha_mode(png->png, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
485
486
308
            if (setjmp(png_jmpbuf(png->png))) {
487
298
                png->state = ii_state_flush;
488
298
                break;
489
298
            }
490
491
            /* We use the "low level" interface to libpng to allow us to
492
             * optimise memory usage. Any errors will longjmp. */
493
10
            png_read_info(png->png, png->png_info);
494
495
10
            png_set_expand_16(png->png);
496
10
            png_set_alpha_mode(png->png, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB);
497
10
            {
498
10
                int bpc, color;
499
10
                static const png_color_16 bg = { 0, 65535, 65535, 65535, 65535 };
500
10
                png_get_IHDR(png->png, png->png_info,
501
10
                             &png->width,
502
10
                             &png->height,
503
10
                             &bpc,
504
10
                             &color,
505
10
                             &png->interlaced,
506
10
                             NULL /* compression */,
507
10
                             NULL /* filter */);
508
10
                switch (color) {
509
0
                case PNG_COLOR_TYPE_GRAY_ALPHA:
510
7
                case PNG_COLOR_TYPE_GRAY:
511
7
                    png->num_comps = 1;
512
7
                    break;
513
8
                case PNG_COLOR_TYPE_PALETTE:
514
8
                    png_set_palette_to_rgb(png->png);
515
8
                    png->num_comps = 3;
516
8
                    break;
517
9
                case PNG_COLOR_TYPE_RGB:
518
30
                case PNG_COLOR_TYPE_RGB_ALPHA:
519
30
                    png->num_comps = 3;
520
30
                    break;
521
0
                default:
522
0
                    png->state = ii_state_flush;
523
0
                    png_longjmp(png->png, 1);
524
0
                    break;
525
10
                }
526
45
                png->passes = png_set_interlace_handling(png->png);
527
45
                png_set_background(png->png, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1);
528
45
            }
529
530
0
            png->xresolution = png_get_x_pixels_per_inch(png->png, png->png_info);
531
45
            png->yresolution = png_get_y_pixels_per_inch(png->png, png->png_info);
532
45
            if (png->xresolution == 0)
533
38
                png->xresolution = png->yresolution;
534
45
            if (png->yresolution == 0)
535
38
                png->yresolution = png->xresolution;
536
45
            if (png->xresolution == 0)
537
38
                png->xresolution = png->yresolution = 72;
538
539
45
            cs = (png->num_comps == 1 ? png->gray : png->rgb);
540
541
            /* Read the updated info */
542
45
            png_read_update_info(png->png, png->png_info);
543
45
            png->bpp = png_get_bit_depth(png->png, png->png_info) * png->num_comps;
544
545
            /* Scale to fit, if too large. */
546
45
            scale = 1.0f;
547
45
            if (png->width * png->dev->HWResolution[0] > png->dev->width * png->xresolution)
548
6
                scale = ((float)png->dev->width * png->xresolution) / (png->width * png->dev->HWResolution[0]);
549
45
            if (scale * png->height * png->dev->HWResolution[1] > png->dev->height * png->yresolution)
550
0
                scale = ((float)png->dev->height * png->yresolution) / (png->height * png->dev->HWResolution[1]);
551
552
            /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
553
45
            xext = ((float)png->width * 72 * scale / png->xresolution);
554
45
            xoffset = (png->dev->width * 72 / png->dev->HWResolution[0] - xext)/2;
555
45
            yext = ((float)png->height * 72 * scale / png->yresolution);
556
45
            yoffset = (png->dev->height * 72 / png->dev->HWResolution[1] - yext)/2;
557
558
            /* Now we've got the data from the image header, we can
559
             * make the gs image instance */
560
45
            png->byte_width = png_get_rowbytes(png->png, png->png_info);
561
562
45
            png->nulldev = gs_currentdevice(png->pgs);
563
45
            rc_increment(png->nulldev);
564
45
            code = gs_setdevice_no_erase(png->pgs, png->dev);
565
45
            if (code < 0) {
566
0
                png->state = ii_state_flush;
567
0
                break;
568
0
            }
569
45
            gs_initmatrix(png->pgs);
570
571
            /* By default the ctm is set to:
572
             *   xres/72   0
573
             *   0         -yres/72
574
             *   0         dev->height * yres/72
575
             * i.e. it moves the origin from being top right to being bottom left.
576
             * We want to move it back, as without this, the image will be displayed
577
             * upside down.
578
             */
579
45
            code = gs_translate(png->pgs, 0.0, png->dev->height * 72 / png->dev->HWResolution[1]);
580
45
            if (code >= 0)
581
45
                code = gs_translate(png->pgs, xoffset, -yoffset);
582
45
            if (code >= 0)
583
45
                code = gs_scale(png->pgs, scale, -scale);
584
            /* At this point, the ctm is set to:
585
             *   xres/72  0
586
             *   0        yres/72
587
             *   0        0
588
             */
589
45
            if (code >= 0)
590
45
                code = gs_erasepage(png->pgs);
591
45
            if (code < 0) {
592
0
                png->state = ii_state_flush;
593
0
                break;
594
0
            }
595
596
45
            if (SIZE_MAX / png->byte_width < (png->interlaced ? png->height : 1))
597
0
            {
598
0
                code = gs_note_error(gs_error_VMerror);
599
0
                png->state = ii_state_flush;
600
0
                break;
601
0
            }
602
603
45
            png->samples = gs_alloc_bytes(png->memory,
604
45
                                          (size_t)png->byte_width * (png->interlaced ? png->height : 1),
605
45
                                          "png_impl_process(samples)");
606
45
            if (png->samples == NULL) {
607
0
                png->state = ii_state_flush;
608
0
                break;
609
0
            }
610
611
45
            memset(&png->image, 0, sizeof(png->image));
612
45
            gs_image_t_init(&png->image, cs);
613
45
            png->image.BitsPerComponent = png->bpp/png->num_comps;
614
45
            png->image.Width = png->width;
615
45
            png->image.Height = png->height;
616
617
45
            png->image.ImageMatrix.xx = png->xresolution / 72.0f;
618
45
            png->image.ImageMatrix.yy = png->yresolution / 72.0f;
619
620
45
            png->penum = gs_image_enum_alloc(png->memory, "png_impl_process(penum)");
621
45
            if (png->penum == NULL) {
622
0
                code = gs_note_error(gs_error_VMerror);
623
0
                png->state = ii_state_flush;
624
0
                return code;
625
0
            }
626
627
45
            code = gs_image_init(png->penum,
628
45
                                 &png->image,
629
45
                                 false,
630
45
                                 false,
631
45
                                 png->pgs);
632
45
            if (code < 0) {
633
0
                png->state = ii_state_flush;
634
0
                return code;
635
0
            }
636
637
45
            {
638
45
                int i, j;
639
640
45
                if (png->interlaced) {
641
                    /* Collect the results from all but the last pass */
642
21
                    for (j = png->passes-1; j > 0; j--)
643
36
                        for (i = 0; i < png->height; i++)
644
18
                            png_read_row(png->png, &png->samples[i*png->byte_width], NULL);
645
646
                    /* And actually process the last pass */
647
6
                    for (i = 0; i < png->height; i++) {
648
3
                        uint used;
649
650
3
                        png_read_row(png->png, &png->samples[i*png->byte_width], NULL);
651
652
3
                        code = gs_image_next(png->penum, &png->samples[i*png->byte_width], png->byte_width, &used);
653
3
                        if (code < 0) {
654
0
                            png->state = ii_state_flush;
655
0
                            break;
656
0
                        }
657
3
                    }
658
42
                } else {
659
2.29k
                    for (i = 0; i < png->height; i++) {
660
2.25k
                        uint used;
661
662
2.25k
                        png_read_row(png->png, png->samples, NULL);
663
664
2.25k
                        code = gs_image_next(png->penum, png->samples, png->byte_width, &used);
665
2.25k
                        if (code < 0) {
666
0
                            png->state = ii_state_flush;
667
0
                            break;
668
0
                        }
669
2.25k
                    }
670
42
                }
671
45
            }
672
673
45
            code = gs_image_cleanup_and_free_enum(png->penum, png->pgs);
674
45
            png->penum = NULL;
675
45
            if (code < 0) {
676
0
                png->state = ii_state_flush;
677
0
                break;
678
0
            }
679
45
            code = pl_finish_page(png->memory->gs_lib_ctx->top_of_system,
680
45
                                  png->pgs, 1, true);
681
45
            if (code < 0) {
682
0
                png->state = ii_state_flush;
683
0
                break;
684
0
            }
685
686
45
            png->state = ii_state_flush;
687
45
            break;
688
45
        }
689
0
        default:
690
308
        case ii_state_flush:
691
308
            if (png->png)
692
308
            {
693
308
                png_destroy_read_struct(&png->png, &png->png_info, NULL);
694
308
                png->png = NULL;
695
308
                png->png_info = NULL;
696
308
            }
697
698
308
            if (png->penum) {
699
35
                (void)gs_image_cleanup_and_free_enum(png->penum, png->pgs);
700
35
                png->penum = NULL;
701
35
            }
702
703
308
            gs_free_object(png->memory, png->buffer, "png_impl_process(buffer)");
704
308
            png->buffer = NULL;
705
308
            gs_free_object(png->memory, png->samples, "png_impl_process(samples)");
706
308
            png->samples = NULL;
707
            /* We want to bin any data we get up to, but not including
708
             * a UEL. */
709
308
            return flush_to_uel(pr);
710
3.22k
        }
711
2.91k
        advanced |= (ostate != png->state);
712
2.91k
        advanced |= (bytes_in != pr->limit - pr->ptr);
713
2.91k
    } while (advanced);
714
715
1.02k
    if (!advanced && bytes == 0)
716
1.02k
        code = gs_note_error(gs_error_NeedInput);
717
718
1.02k
    return code;
719
1.33k
}
720
721
static int
722
png_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
723
1.02k
{
724
1.02k
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
725
726
1.02k
    return do_impl_process(png, pr, 0);
727
1.02k
}
728
729
static int
730
png_impl_process_end(pl_interp_implementation_t * impl)
731
308
{
732
308
    return 0;
733
308
}
734
735
/* Not implemented */
736
static int
737
png_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
738
0
{
739
0
    const byte *p = cursor->ptr;
740
0
    const byte *rlimit = cursor->limit;
741
742
    /* Skip to, but leave UEL in buffer for PJL to find later */
743
0
    for (; p < rlimit; ++p)
744
0
        if (p[1] == '\033') {
745
0
            uint avail = rlimit - p;
746
747
0
            if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
748
0
                continue;
749
0
            if (avail < 9)
750
0
                break;
751
0
            cursor->ptr = p;
752
0
            return 1;           /* found eoj */
753
0
        }
754
0
    cursor->ptr = p;
755
0
    return 0;                   /* need more */
756
0
}
757
758
/* Parser action for end-of-file */
759
static int
760
png_impl_process_eof(pl_interp_implementation_t *impl)
761
308
{
762
308
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
763
308
    stream_cursor_read cursor;
764
765
308
    cursor.ptr = NULL;
766
308
    cursor.limit = NULL;
767
768
308
    return do_impl_process(png, &cursor, 1);
769
308
}
770
771
/* Report any errors after running a job */
772
static int
773
png_impl_report_errors(pl_interp_implementation_t *impl,          /* interp instance to wrap up job in */
774
                       int                         code,          /* prev termination status */
775
                       long                        file_position, /* file position of error, -1 if unknown */
776
                       bool                        force_to_cout  /* force errors to cout */
777
)
778
0
{
779
0
    return 0;
780
0
}
781
782
/* Wrap up interp instance after a "job" */
783
static int
784
png_impl_dnit_job(pl_interp_implementation_t *impl)
785
308
{
786
308
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
787
788
308
    if (png->nulldev) {
789
45
        int code = gs_setdevice(png->pgs, png->nulldev);
790
45
        png->dev = NULL;
791
45
        rc_decrement(png->nulldev, "png_impl_dnit_job(nulldevice)");
792
45
        png->nulldev = NULL;
793
45
        return code;
794
45
    }
795
263
    return 0;
796
308
}
797
798
/* Parser implementation descriptor */
799
const pl_interp_implementation_t png_implementation = {
800
  png_impl_characteristics,
801
  png_impl_allocate_interp_instance,
802
  png_impl_get_device_memory,
803
  NULL, /* png_impl_set_param */
804
  NULL, /* png_impl_add_path */
805
  NULL, /* png_impl_post_args_init */
806
  png_impl_init_job,
807
  NULL, /* png_impl_run_prefix_commands */
808
  NULL, /* png_impl_process_file */
809
  png_impl_process_begin,
810
  png_impl_process,
811
  png_impl_process_end,
812
  png_impl_flush_to_eoj,
813
  png_impl_process_eof,
814
  png_impl_report_errors,
815
  png_impl_dnit_job,
816
  png_impl_deallocate_interp_instance,
817
  NULL, /* png_impl_reset */
818
  NULL  /* interp_client_data */
819
};