Coverage Report

Created: 2026-09-14 07:34

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
22.7k
{
95
22.7k
    const byte *hdr = (const byte *)s;
96
22.7k
    if (len >= 8) {
97
22.2k
        if (hdr[0] == 137 &&
98
371
            hdr[1] == 80 &&
99
364
            hdr[2] == 78 &&
100
361
            hdr[3] == 71 &&
101
360
            hdr[4] == 13 &&
102
359
            hdr[5] == 10 &&
103
359
            hdr[6] == 26 &&
104
358
            hdr[7] == 10)
105
357
            return 100;
106
22.2k
    }
107
108
22.3k
    return 0;
109
22.7k
}
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
48.4k
{
120
48.4k
  return &png_characteristics;
121
48.4k
}
122
123
static void
124
png_deallocate(png_interp_instance_t *png)
125
11.0k
{
126
11.0k
    if (png == NULL)
127
0
        return;
128
129
11.0k
    rc_decrement_cs(png->gray, "png_deallocate");
130
11.0k
    rc_decrement_cs(png->rgb, "png_deallocate");
131
132
11.0k
    if (png->pgs != NULL)
133
11.0k
        gs_gstate_free_chain(png->pgs);
134
11.0k
    gs_free_object(png->memory, png, "png_impl_allocate_interp_instance");
135
11.0k
}
136
137
/* Deallocate a interpreter instance */
138
static int
139
png_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
140
11.0k
{
141
11.0k
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
142
143
11.0k
    png_deallocate(png);
144
11.0k
    impl->interp_client_data = NULL;
145
146
11.0k
    return 0;
147
11.0k
}
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
11.0k
{
153
11.0k
    int code;
154
11.0k
    png_interp_instance_t *png
155
11.0k
        = (png_interp_instance_t *)gs_alloc_bytes(mem,
156
11.0k
                                                  sizeof(png_interp_instance_t),
157
11.0k
                                                  "png_impl_allocate_interp_instance");
158
11.0k
    if (!png)
159
0
        return_error(gs_error_VMerror);
160
11.0k
    memset(png, 0, sizeof(*png));
161
162
11.0k
    png->memory = mem;
163
11.0k
    png->pgs = gs_gstate_alloc(mem);
164
11.0k
    if (png->pgs == NULL)
165
0
        goto failVM;
166
167
    /* Push one save level onto the stack to assuage the memory handling */
168
11.0k
    code = gs_gsave(png->pgs);
169
11.0k
    if (code < 0)
170
0
        goto fail;
171
172
11.0k
    code = gsicc_init_iccmanager(png->pgs);
173
11.0k
    if (code < 0)
174
0
        goto fail;
175
176
11.0k
    png->gray = gs_cspace_new_ICC(mem, png->pgs, 1);
177
11.0k
    png->rgb  = gs_cspace_new_ICC(mem, png->pgs, 3);
178
179
11.0k
    impl->interp_client_data = png;
180
181
11.0k
    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
350
{
206
350
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
207
208
350
    png->dev = device;
209
350
    png->state = ii_state_identifying;
210
211
350
    return 0;
212
350
}
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
350
{
218
350
    return 0;
219
350
}
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
350
{
226
350
    int n;
227
350
    const uint8_t *p = pr->ptr+1;
228
350
    const uint8_t *q;
229
350
    int avail;
230
231
    /* Find out how many bytes we need to check */
232
350
    n = pr->limit - pr->ptr;
233
350
    if (n > required)
234
349
        n = required;
235
236
    /* Make sure there are no UELs in that block */
237
350
    q = p + n;
238
350
    while (p != q) {
239
3.15k
        while (p != q && *p != '\033')
240
2.80k
            p++;
241
350
        if (p == q)
242
350
            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
350
    return (n < required) ? gs_error_NeedInput : 0;
253
350
}
254
255
static int
256
flush_to_uel(stream_cursor_read *pr)
257
350
{
258
350
    const uint8_t *p = pr->ptr+1;
259
350
    const uint8_t *q = pr->limit+1;
260
350
    int avail;
261
262
350
    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
350
    pr->ptr = pr->limit;
281
282
350
    return 0;
283
350
}
284
285
static int
286
bytes_until_uel(const stream_cursor_read *pr)
287
1.73k
{
288
1.73k
    const uint8_t *p = pr->ptr+1;
289
1.73k
    const uint8_t *q = pr->limit+1;
290
1.73k
    int avail;
291
292
3.86k
    while (p != q) {
293
864k
        while (p != q && *p != '\033')
294
862k
            p++;
295
2.80k
        if (p == q)
296
660
            break;
297
2.14k
        avail = q - p;
298
2.14k
        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
2.12k
        p++;
304
2.12k
    }
305
306
1.72k
    return pr->limit - pr->ptr;
307
1.73k
}
308
309
OPTIMIZE_SETJMP
310
static void
311
my_png_error(png_structp png_ptr, png_const_charp error_msg)
312
344
{
313
    /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */
314
315
344
    png_longjmp(png_ptr, 1);
316
344
}
317
318
static void
319
my_png_warning(png_structp png_ptr, png_const_charp warning_msg)
320
15.5k
{
321
    /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */
322
15.5k
}
323
324
static png_voidp
325
my_png_malloc(png_structp png_ptr, png_alloc_size_t size)
326
9.98k
{
327
9.98k
    png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr);
328
329
9.98k
    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
9.98k
        unsigned char *block = gs_alloc_bytes(png->memory, size+16, "my_png_malloc");
333
9.98k
        intptr_t num_bytes_padded;
334
335
9.98k
        if (block == NULL)
336
0
            return NULL;
337
9.98k
        num_bytes_padded = 16-(((intptr_t)block) & 15);
338
9.98k
        block += num_bytes_padded;
339
9.98k
        block[-1] = num_bytes_padded;
340
341
9.98k
        return block;
342
9.98k
    }
343
0
    return gs_alloc_bytes(png->memory, size, "my_png_malloc");
344
9.98k
}
345
346
static void
347
my_png_free(png_structp png_ptr, png_voidp ptr)
348
9.98k
{
349
9.98k
    png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr);
350
351
9.98k
    if (sizeof(void *) == 8) {
352
9.98k
        unsigned char *block = ptr;
353
9.98k
        if (block == NULL)
354
0
            return;
355
9.98k
        block -= block[-1];
356
9.98k
        ptr = (void *)block;
357
9.98k
    }
358
9.98k
    gs_free_object(png->memory, ptr, "my_png_free");
359
9.98k
}
360
361
static void
362
my_png_read(png_structp png_ptr, png_bytep data, png_size_t length)
363
45.0k
{
364
45.0k
    png_interp_instance_t *png = (png_interp_instance_t *)png_get_io_ptr(png_ptr);
365
45.0k
    if (length + png->file_pos > png->buffer_full)
366
281
        png_error(png_ptr, "Overread!");
367
368
44.7k
    memcpy(data, &png->buffer[png->file_pos], length);
369
44.7k
    png->file_pos += length;
370
44.7k
}
371
372
OPTIMIZE_SETJMP
373
static int
374
do_impl_process(png_interp_instance_t *png, stream_cursor_read * pr, bool eof)
375
1.07k
{
376
1.07k
    int code = 0, bytes;
377
1.07k
    ii_state ostate;
378
1.07k
    size_t bytes_in;
379
1.07k
    int advanced;
380
381
    /* Loop until we stop 'advancing'. */
382
1.07k
    do
383
2.78k
    {
384
2.78k
        ostate = png->state;
385
2.78k
        bytes_in = pr->limit - pr->ptr;
386
2.78k
        advanced = 0;
387
2.78k
        switch(png->state)
388
2.78k
        {
389
350
        case ii_state_identifying:
390
350
        {
391
350
            const byte *hdr;
392
            /* Try and get us 8 bytes */
393
350
            code = ensure_bytes(png, pr, 8);
394
350
            if (code < 0)
395
0
                return code;
396
350
            hdr = pr->ptr+1;
397
350
            if (hdr[0] == 137 &&
398
350
                hdr[1] == 80 &&
399
350
                hdr[2] == 78 &&
400
350
                hdr[3] == 71 &&
401
350
                hdr[4] == 13 &&
402
350
                hdr[5] == 10 &&
403
350
                hdr[6] == 26 &&
404
350
                hdr[7] == 10) {
405
350
                png->state = ii_state_png;
406
350
                break;
407
350
            }
408
0
            png->state = ii_state_flush;
409
0
            break;
410
350
        }
411
1.73k
        case ii_state_png:
412
1.73k
        {
413
            /* Gather data into a buffer */
414
1.73k
            bytes = bytes_until_uel(pr);
415
416
1.73k
            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
1.73k
            if (bytes == 0 && eof) {
423
                /* No bytes until UEL, and we are at eof */
424
350
                png->state = ii_state_png_decode;
425
350
                png->file_pos = 0;
426
350
                break;
427
350
            }
428
429
1.38k
            if (png->buffer_full + bytes > png->buffer_max) {
430
                /* Need to expand our buffer */
431
352
                size_t proposed = png->buffer_full*2;
432
352
                if (proposed == 0)
433
350
                    proposed = 32768;
434
352
                while (proposed < png->buffer_full + bytes)
435
0
                    proposed *= 2;
436
437
352
                if (png->buffer == NULL) {
438
350
                    png->buffer = gs_alloc_bytes(png->memory, proposed, "png_buffer");
439
350
                    if (png->buffer == NULL) {
440
0
                        png->state = ii_state_flush;
441
0
                        break;
442
0
                    }
443
350
                } else {
444
2
                    void *new_buf = gs_resize_object(png->memory, png->buffer, proposed, "png_buffer");
445
2
                    if (new_buf == NULL) {
446
0
                        png->state = ii_state_flush;
447
0
                        break;
448
0
                    }
449
2
                    png->buffer = new_buf;
450
2
                }
451
352
                png->buffer_max = proposed;
452
352
            }
453
454
1.38k
            memcpy(&png->buffer[png->buffer_full], pr->ptr+1, bytes);
455
1.38k
            png->buffer_full += bytes;
456
1.38k
            pr->ptr += bytes;
457
1.38k
            break;
458
1.38k
        }
459
350
        case ii_state_png_decode:
460
350
        {
461
350
            gs_color_space *cs;
462
350
            float scale, xext, yext, xoffset, yoffset;
463
464
350
            png->png = png_create_read_struct_2(
465
350
                               PNG_LIBPNG_VER_STRING,
466
350
                               (png_voidp)png,
467
350
                               my_png_error,
468
350
                               my_png_warning,
469
350
                               (png_voidp)png,
470
350
                               my_png_malloc,
471
350
                               my_png_free);
472
350
            if (png->png == NULL) {
473
0
                png->state = ii_state_flush;
474
0
                break;
475
0
            }
476
477
350
            png->png_info = png_create_info_struct(png->png);
478
350
            if (!png->png_info) {
479
0
                png->state = ii_state_flush;
480
0
                break;
481
0
            }
482
483
350
            png_set_read_fn(png->png, png, my_png_read);
484
350
            png_set_alpha_mode(png->png, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
485
486
350
            if (setjmp(png_jmpbuf(png->png))) {
487
344
                png->state = ii_state_flush;
488
344
                break;
489
344
            }
490
491
            /* We use the "low level" interface to libpng to allow us to
492
             * optimise memory usage. Any errors will longjmp. */
493
6
            png_read_info(png->png, png->png_info);
494
495
6
            png_set_expand_16(png->png);
496
6
            png_set_alpha_mode(png->png, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB);
497
6
            {
498
6
                int bpc, color;
499
6
                static const png_color_16 bg = { 0, 65535, 65535, 65535, 65535 };
500
6
                png_get_IHDR(png->png, png->png_info,
501
6
                             &png->width,
502
6
                             &png->height,
503
6
                             &bpc,
504
6
                             &color,
505
6
                             &png->interlaced,
506
6
                             NULL /* compression */,
507
6
                             NULL /* filter */);
508
6
                switch (color) {
509
2
                case PNG_COLOR_TYPE_GRAY_ALPHA:
510
17
                case PNG_COLOR_TYPE_GRAY:
511
17
                    png->num_comps = 1;
512
17
                    break;
513
4
                case PNG_COLOR_TYPE_PALETTE:
514
4
                    png_set_palette_to_rgb(png->png);
515
4
                    png->num_comps = 3;
516
4
                    break;
517
15
                case PNG_COLOR_TYPE_RGB:
518
27
                case PNG_COLOR_TYPE_RGB_ALPHA:
519
27
                    png->num_comps = 3;
520
27
                    break;
521
0
                default:
522
0
                    png->state = ii_state_flush;
523
0
                    png_longjmp(png->png, 1);
524
0
                    break;
525
6
                }
526
48
                png->passes = png_set_interlace_handling(png->png);
527
48
                png_set_background(png->png, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1);
528
48
            }
529
530
0
            png->xresolution = png_get_x_pixels_per_inch(png->png, png->png_info);
531
48
            png->yresolution = png_get_y_pixels_per_inch(png->png, png->png_info);
532
48
            if (png->xresolution == 0)
533
43
                png->xresolution = png->yresolution;
534
48
            if (png->yresolution == 0)
535
43
                png->yresolution = png->xresolution;
536
48
            if (png->xresolution == 0)
537
43
                png->xresolution = png->yresolution = 72;
538
539
48
            cs = (png->num_comps == 1 ? png->gray : png->rgb);
540
541
            /* Read the updated info */
542
48
            png_read_update_info(png->png, png->png_info);
543
48
            png->bpp = png_get_bit_depth(png->png, png->png_info) * png->num_comps;
544
545
            /* Scale to fit, if too large. */
546
48
            scale = 1.0f;
547
48
            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
48
            if (scale * png->height * png->dev->HWResolution[1] > png->dev->height * png->yresolution)
550
3
                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
48
            xext = ((float)png->width * 72 * scale / png->xresolution);
554
48
            xoffset = (png->dev->width * 72 / png->dev->HWResolution[0] - xext)/2;
555
48
            yext = ((float)png->height * 72 * scale / png->yresolution);
556
48
            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
48
            png->byte_width = png_get_rowbytes(png->png, png->png_info);
561
562
48
            png->nulldev = gs_currentdevice(png->pgs);
563
48
            rc_increment(png->nulldev);
564
48
            code = gs_setdevice_no_erase(png->pgs, png->dev);
565
48
            if (code < 0) {
566
0
                png->state = ii_state_flush;
567
0
                break;
568
0
            }
569
48
            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
48
            code = gs_translate(png->pgs, 0.0, png->dev->height * 72 / png->dev->HWResolution[1]);
580
48
            if (code >= 0)
581
48
                code = gs_translate(png->pgs, xoffset, -yoffset);
582
48
            if (code >= 0)
583
48
                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
48
            if (code >= 0)
590
48
                code = gs_erasepage(png->pgs);
591
48
            if (code < 0) {
592
0
                png->state = ii_state_flush;
593
0
                break;
594
0
            }
595
596
48
            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
48
            png->samples = gs_alloc_bytes(png->memory,
604
48
                                          (size_t)png->byte_width * (png->interlaced ? png->height : 1),
605
48
                                          "png_impl_process(samples)");
606
48
            if (png->samples == NULL) {
607
0
                png->state = ii_state_flush;
608
0
                break;
609
0
            }
610
611
48
            memset(&png->image, 0, sizeof(png->image));
612
48
            gs_image_t_init(&png->image, cs);
613
48
            png->image.BitsPerComponent = png->bpp/png->num_comps;
614
48
            png->image.Width = png->width;
615
48
            png->image.Height = png->height;
616
617
48
            png->image.ImageMatrix.xx = png->xresolution / 72.0f;
618
48
            png->image.ImageMatrix.yy = png->yresolution / 72.0f;
619
620
48
            png->penum = gs_image_enum_alloc(png->memory, "png_impl_process(penum)");
621
48
            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
48
            code = gs_image_init(png->penum,
628
48
                                 &png->image,
629
48
                                 false,
630
48
                                 false,
631
48
                                 png->pgs);
632
48
            if (code < 0) {
633
0
                png->state = ii_state_flush;
634
0
                return code;
635
0
            }
636
637
48
            {
638
48
                int i, j;
639
640
48
                if (png->interlaced) {
641
                    /* Collect the results from all but the last pass */
642
63
                    for (j = png->passes-1; j > 0; j--)
643
4.12k
                        for (i = 0; i < png->height; i++)
644
4.06k
                            png_read_row(png->png, &png->samples[i*png->byte_width], NULL);
645
646
                    /* And actually process the last pass */
647
67
                    for (i = 0; i < png->height; i++) {
648
56
                        uint used;
649
650
56
                        png_read_row(png->png, &png->samples[i*png->byte_width], NULL);
651
652
56
                        code = gs_image_next(png->penum, &png->samples[i*png->byte_width], png->byte_width, &used);
653
56
                        if (code < 0) {
654
0
                            png->state = ii_state_flush;
655
0
                            break;
656
0
                        }
657
56
                    }
658
37
                } else {
659
4.11k
                    for (i = 0; i < png->height; i++) {
660
4.07k
                        uint used;
661
662
4.07k
                        png_read_row(png->png, png->samples, NULL);
663
664
4.07k
                        code = gs_image_next(png->penum, png->samples, png->byte_width, &used);
665
4.07k
                        if (code < 0) {
666
0
                            png->state = ii_state_flush;
667
0
                            break;
668
0
                        }
669
4.07k
                    }
670
37
                }
671
48
            }
672
673
48
            code = gs_image_cleanup_and_free_enum(png->penum, png->pgs);
674
48
            png->penum = NULL;
675
48
            if (code < 0) {
676
0
                png->state = ii_state_flush;
677
0
                break;
678
0
            }
679
48
            code = pl_finish_page(png->memory->gs_lib_ctx->top_of_system,
680
48
                                  png->pgs, 1, true);
681
48
            if (code < 0) {
682
0
                png->state = ii_state_flush;
683
0
                break;
684
0
            }
685
686
48
            png->state = ii_state_flush;
687
48
            break;
688
48
        }
689
0
        default:
690
350
        case ii_state_flush:
691
350
            if (png->png)
692
350
            {
693
350
                png_destroy_read_struct(&png->png, &png->png_info, NULL);
694
350
                png->png = NULL;
695
350
                png->png_info = NULL;
696
350
            }
697
698
350
            if (png->penum) {
699
42
                (void)gs_image_cleanup_and_free_enum(png->penum, png->pgs);
700
42
                png->penum = NULL;
701
42
            }
702
703
350
            gs_free_object(png->memory, png->buffer, "png_impl_process(buffer)");
704
350
            png->buffer = NULL;
705
350
            gs_free_object(png->memory, png->samples, "png_impl_process(samples)");
706
350
            png->samples = NULL;
707
            /* We want to bin any data we get up to, but not including
708
             * a UEL. */
709
350
            return flush_to_uel(pr);
710
2.78k
        }
711
2.43k
        advanced |= (ostate != png->state);
712
2.43k
        advanced |= (bytes_in != pr->limit - pr->ptr);
713
2.43k
    } while (advanced);
714
715
722
    if (!advanced && bytes == 0)
716
722
        code = gs_note_error(gs_error_NeedInput);
717
718
722
    return code;
719
1.07k
}
720
721
static int
722
png_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
723
722
{
724
722
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
725
726
722
    return do_impl_process(png, pr, 0);
727
722
}
728
729
static int
730
png_impl_process_end(pl_interp_implementation_t * impl)
731
350
{
732
350
    return 0;
733
350
}
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
350
{
762
350
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
763
350
    stream_cursor_read cursor;
764
765
350
    cursor.ptr = NULL;
766
350
    cursor.limit = NULL;
767
768
350
    return do_impl_process(png, &cursor, 1);
769
350
}
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
350
{
786
350
    png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data;
787
788
350
    if (png->nulldev) {
789
48
        int code = gs_setdevice(png->pgs, png->nulldev);
790
48
        png->dev = NULL;
791
48
        rc_decrement(png->nulldev, "png_impl_dnit_job(nulldevice)");
792
48
        png->nulldev = NULL;
793
48
        return code;
794
48
    }
795
302
    return 0;
796
350
}
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
};