Coverage Report

Created: 2026-04-01 07:17

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