Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/gpdl/jp2ktop.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
/* jp2ktop.c */
17
/* Top-level API implementation of "JP2K" 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 "sjpx_openjpeg.h"
29
#include "stream.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_jp2k,
41
    ii_state_jp2k_header,
42
    ii_state_jp2k_data,
43
    ii_state_flush
44
} ii_state;
45
46
/*
47
 * JP2K interpreter instance
48
 */
49
typedef struct jp2k_interp_instance_s {
50
    gs_memory_t       *memory;
51
    gx_device         *dev;
52
    gx_device         *nulldev;
53
54
    gs_color_space    *gray;
55
    gs_color_space    *rgb;
56
    gs_color_space    *cmyk;
57
58
    /* PWG parser state machine */
59
    ii_state           state;
60
61
    int                pages;
62
63
    uint32_t           bpp;
64
    uint32_t           width;
65
    uint32_t           height;
66
    uint32_t           xresolution;
67
    uint32_t           yresolution;
68
    uint32_t           copies;
69
70
    uint32_t           num_comps;
71
    uint32_t           byte_width;
72
    uint32_t           x;
73
    uint32_t           y;
74
75
    gs_image_t         image;
76
    gs_image_enum     *penum;
77
    gs_gstate         *pgs;
78
79
    stream_jpxd_state  jp2k_state;
80
    byte               stream_buffer[2048];
81
82
} jp2k_interp_instance_t;
83
84
static int
85
jp2k_detect_language(const char *s_, int len)
86
28.0k
{
87
28.0k
    const unsigned char *s = (const unsigned char *)s_;
88
28.0k
    if (len >= 12) {
89
27.3k
        if (s[0] == 0x00 &&
90
923
            s[1] == 0x00 &&
91
356
            s[2] == 0x00 &&
92
254
            s[3] == 0x0C &&
93
51
            s[4] == 0x6a &&
94
38
            s[5] == 0x50 &&
95
38
            ((s[6] == 0x1a && s[7] == 0x1a) || (s[6] == 0x20 && s[7] == 0x20)) &&
96
37
            s[8] == 0x0d &&
97
36
            s[9] == 0x0a &&
98
35
            s[10] == 0x87 &&
99
35
            s[11] == 0x0a)
100
34
            return 100; /* JP2 file */
101
27.3k
    }
102
28.0k
    if (len >= 4) {
103
27.9k
        if (s[0] == 0xff &&
104
353
            s[1] == 0x4f &&
105
70
            s[2] == 0xff &&
106
67
            s[3] == 0x51)
107
66
            return 100; /* J2K stream */
108
27.9k
    }
109
110
27.9k
    return 0;
111
28.0k
}
112
113
static const pl_interp_characteristics_t jp2k_characteristics = {
114
    "JP2K",
115
    jp2k_detect_language,
116
};
117
118
/* Get implementation's characteristics */
119
static const pl_interp_characteristics_t * /* always returns a descriptor */
120
jp2k_impl_characteristics(const pl_interp_implementation_t *impl)     /* implementation of interpreter to alloc */
121
59.1k
{
122
59.1k
  return &jp2k_characteristics;
123
59.1k
}
124
125
static void
126
jp2k_deallocate(jp2k_interp_instance_t *jp2k)
127
16.1k
{
128
16.1k
    if (jp2k == NULL)
129
0
        return;
130
131
16.1k
    rc_decrement_cs(jp2k->gray, "jp2k_deallocate");
132
16.1k
    rc_decrement_cs(jp2k->rgb, "jp2k_deallocate");
133
16.1k
    rc_decrement_cs(jp2k->cmyk, "jp2k_deallocate");
134
135
16.1k
    if (jp2k->pgs != NULL)
136
16.1k
        gs_gstate_free_chain(jp2k->pgs);
137
16.1k
    gs_free_object(jp2k->memory, jp2k, "jp2k_impl_allocate_interp_instance");
138
16.1k
}
139
140
/* Deallocate a interpreter instance */
141
static int
142
jp2k_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
143
16.1k
{
144
16.1k
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
145
146
16.1k
    jp2k_deallocate(jp2k);
147
16.1k
    impl->interp_client_data = NULL;
148
149
16.1k
    return 0;
150
16.1k
}
151
152
/* Do per-instance interpreter allocation/init. */
153
static int
154
jp2k_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
155
16.1k
{
156
16.1k
    int code;
157
16.1k
    jp2k_interp_instance_t *jp2k
158
16.1k
        = (jp2k_interp_instance_t *)gs_alloc_bytes(mem,
159
16.1k
                                                  sizeof(jp2k_interp_instance_t),
160
16.1k
                                                  "jp2k_impl_allocate_interp_instance");
161
16.1k
    if (!jp2k)
162
0
        return_error(gs_error_VMerror);
163
16.1k
    memset(jp2k, 0, sizeof(*jp2k));
164
165
16.1k
    jp2k->memory = mem;
166
16.1k
    jp2k->pgs = gs_gstate_alloc(mem);
167
16.1k
    if (jp2k->pgs == NULL)
168
0
        goto failVM;
169
170
    /* Push one save level onto the stack to assuage the memory handling */
171
16.1k
    code = gs_gsave(jp2k->pgs);
172
16.1k
    if (code < 0)
173
0
        goto fail;
174
175
16.1k
    code = gsicc_init_iccmanager(jp2k->pgs);
176
16.1k
    if (code < 0)
177
0
        goto fail;
178
179
16.1k
    jp2k->gray = gs_cspace_new_ICC(mem, jp2k->pgs, 1);
180
16.1k
    jp2k->rgb  = gs_cspace_new_ICC(mem, jp2k->pgs, 3);
181
16.1k
    jp2k->cmyk = gs_cspace_new_ICC(mem, jp2k->pgs, 4);
182
183
16.1k
    impl->interp_client_data = jp2k;
184
185
16.1k
    return 0;
186
187
0
failVM:
188
0
    code = gs_note_error(gs_error_VMerror);
189
0
fail:
190
0
    (void)jp2k_deallocate(jp2k);
191
0
    return code;
192
0
}
193
194
/*
195
 * Get the allocator with which to allocate a device
196
 */
197
static gs_memory_t *
198
jp2k_impl_get_device_memory(pl_interp_implementation_t *impl)
199
0
{
200
0
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
201
202
0
    return jp2k->dev ? jp2k->dev->memory : NULL;
203
0
}
204
205
#if 0 /* UNUSED */
206
static int
207
jp2k_impl_set_param(pl_interp_implementation_t *impl,
208
                   pl_set_param_type           type,
209
                   const char                 *param,
210
                   const void                 *val)
211
{
212
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
213
214
    /* No params set here */
215
    return 0;
216
}
217
218
static int
219
jp2k_impl_add_path(pl_interp_implementation_t *impl,
220
                  const char                 *path)
221
{
222
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
223
224
    /* No paths to add */
225
    return 0;
226
}
227
228
static int
229
jp2k_impl_post_args_init(pl_interp_implementation_t *impl)
230
{
231
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
232
233
    /* No post args processing */
234
    return 0;
235
}
236
#endif
237
238
/* Prepare interp instance for the next "job" */
239
static int
240
jp2k_impl_init_job(pl_interp_implementation_t *impl,
241
                  gx_device                  *device)
242
100
{
243
100
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
244
245
100
    jp2k->dev = device;
246
100
    jp2k->state = ii_state_identifying;
247
248
100
    return 0;
249
100
}
250
251
#if 0 /* UNUSED */
252
static int
253
jp2k_impl_run_prefix_commands(pl_interp_implementation_t *impl,
254
                             const char                 *prefix)
255
{
256
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
257
258
    return 0;
259
}
260
261
static int
262
jp2k_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
263
{
264
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
265
266
    return 0;
267
}
268
#endif
269
270
/* Do any setup for parser per-cursor */
271
static int                      /* ret 0 or +ve if ok, else -ve error code */
272
jp2k_impl_process_begin(pl_interp_implementation_t * impl)
273
100
{
274
100
    return 0;
275
100
}
276
277
/* Ensure we have 'required' bytes to read, and further ensure
278
 * that we have no UEL's within those bytes. */
279
static int
280
ensure_bytes(jp2k_interp_instance_t *jp2k, stream_cursor_read *pr, int required)
281
134
{
282
134
    int n;
283
134
    const uint8_t *p = pr->ptr+1;
284
134
    const uint8_t *q;
285
134
    int avail;
286
287
    /* Find out how many bytes we need to check */
288
134
    n = pr->limit - pr->ptr;
289
134
    if (n > required)
290
133
        n = required;
291
292
    /* Make sure there are no UELs in that block */
293
134
    q = p + n;
294
134
    while (p != q) {
295
942
        while (p != q && *p != '\033')
296
808
            p++;
297
134
        if (p == q)
298
134
            break;
299
0
        avail = pr->limit - pr->ptr;
300
0
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
301
            /* At least a partial match to a UEL */
302
0
            return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit;
303
0
        }
304
0
        p++;
305
0
    }
306
307
    /* If we have enough bytes, great, if not, get some more */
308
134
    return (n < required) ? gs_error_NeedInput : 0;
309
134
}
310
311
static int
312
flush_to_uel(stream_cursor_read *pr)
313
2
{
314
2
    const uint8_t *p = pr->ptr+1;
315
2
    const uint8_t *q = pr->limit+1;
316
2
    int avail;
317
318
2
    while (p != q) {
319
0
        while (p != q && *p != '\033')
320
0
            p++;
321
0
        if (p == q)
322
0
            break;
323
0
        avail = pr->limit - pr->ptr;
324
0
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
325
            /* At least a partial match to a UEL. Bin everything to
326
             * the start of the match. */
327
0
            pr->ptr = p-1;
328
0
            if (avail == 9) /* Complete match. Exit! */
329
0
                return gs_error_InterpreterExit;
330
            /* Partial match. Get more data. */
331
0
            return gs_error_NeedInput;
332
0
        }
333
0
        p++;
334
0
    }
335
336
2
    pr->ptr = pr->limit;
337
338
2
    return 0;
339
2
}
340
341
static int
342
bytes_until_uel(const stream_cursor_read *pr)
343
1.22k
{
344
1.22k
    const uint8_t *p = pr->ptr+1;
345
1.22k
    const uint8_t *q = pr->limit+1;
346
1.22k
    int avail;
347
348
4.59k
    while (p != q) {
349
996k
        while (p != q && *p != '\033')
350
993k
            p++;
351
3.93k
        if (p == q)
352
545
            break;
353
3.39k
        avail = q - p;
354
3.39k
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
355
            /* At least a partial match to a UEL. Everything up to
356
             * the start of the match is up for grabs. */
357
22
            return p - (pr->ptr+1);
358
22
        }
359
3.37k
        p++;
360
3.37k
    }
361
362
1.20k
    return pr->limit - pr->ptr;
363
1.22k
}
364
365
static int
366
do_process(jp2k_interp_instance_t *jp2k, stream_cursor_read * pr, bool eof)
367
668
{
368
668
    int code = 0;
369
668
    gs_color_space *cs;
370
668
    ii_state ostate;
371
668
    size_t bytes_in;
372
668
    int advanced;
373
374
    /* Loop until we stop 'advancing'. */
375
668
    do
376
1.42k
    {
377
1.42k
        advanced = 0;
378
1.42k
        ostate = jp2k->state;
379
1.42k
        bytes_in = pr->limit - pr->ptr;
380
1.42k
        switch(jp2k->state)
381
1.42k
        {
382
100
        case ii_state_identifying:
383
100
        {
384
100
            const byte *s = pr->ptr+1;
385
386
100
            code = ensure_bytes(jp2k, pr, 4);
387
100
            if (code < 0)
388
0
                return code;
389
100
            if (s[0] == 0xff &&
390
66
                s[1] == 0x4f &&
391
66
                s[2] == 0xff &&
392
66
                s[3] == 0x51) {
393
66
                jp2k->state = ii_state_jp2k_header;
394
66
                break;
395
66
            }
396
397
34
            code = ensure_bytes(jp2k, pr, 12);
398
34
            if (code < 0)
399
0
                return code;
400
34
            if (s[0] == 0x00 &&
401
34
                s[1] == 0x00 &&
402
34
                s[2] == 0x00 &&
403
34
                s[3] == 0x0C &&
404
34
                s[4] == 0x6a &&
405
34
                s[5] == 0x50 &&
406
34
                ((s[6] == 0x1a && s[7] == 0x1a) || (s[6] == 0x20 && s[7] == 0x20)) &&
407
34
                s[8] == 0x0d &&
408
34
                s[9] == 0x0a &&
409
34
                s[10] == 0x87 &&
410
34
                s[11] == 0x0a) {
411
34
                jp2k->state = ii_state_jp2k_header;
412
34
                break;
413
34
            }
414
0
            jp2k->state = ii_state_flush;
415
0
            break;
416
34
        }
417
101
        case ii_state_jp2k_header:
418
101
            s_init_state((stream_state *)&jp2k->jp2k_state, &s_jpxd_template, jp2k->memory);
419
101
            if (s_jpxd_template.set_defaults)
420
101
                s_jpxd_template.set_defaults((stream_state *)&jp2k->jp2k_state);
421
422
101
            code = (s_jpxd_template.init)((stream_state *)&jp2k->jp2k_state);
423
101
            if (code < 0)
424
0
                goto early_flush;
425
101
            jp2k->state = ii_state_jp2k_data;
426
101
            break;
427
1.22k
        case ii_state_jp2k_data:
428
1.22k
        {
429
1.22k
            int n = bytes_until_uel(pr);
430
1.22k
            stream_cursor_read local_r;
431
1.22k
            stream_cursor_write local_w;
432
1.22k
            int status;
433
1.22k
            unsigned int used;
434
435
1.22k
            local_r.ptr = pr->ptr;
436
1.22k
            local_r.limit = local_r.ptr + n;
437
438
1.22k
            do {
439
1.22k
                local_w.ptr = jp2k->stream_buffer-1;
440
1.22k
                local_w.limit = local_w.ptr + sizeof(jp2k->stream_buffer);
441
442
1.22k
                status = (s_jpxd_template.process)((stream_state *)&jp2k->jp2k_state,
443
1.22k
                                                                   &local_r, &local_w,
444
1.22k
                                                                   eof);
445
                /* status = 0 => need data
446
                 *          1 => need output space
447
                 * but we don't actually use this currently. */
448
1.22k
                (void)status;
449
                /* Copy the updated pointer back */
450
1.22k
                pr->ptr = local_r.ptr;
451
1.22k
                if (local_w.ptr + 1 == jp2k->stream_buffer)
452
1.22k
                    break; /* Failed to decode any data */
453
454
3
                if (jp2k->width == 0) {
455
3
                    float scale, xoffset, yoffset, xext, yext;
456
457
                    /* First data we decoded! */
458
3
                    jp2k->width = jp2k->jp2k_state.width;
459
3
                    if (jp2k->width == 0) {
460
0
                        jp2k->state = ii_state_flush;
461
0
                        break;
462
0
                    }
463
3
                    jp2k->height = jp2k->jp2k_state.height;
464
3
                    jp2k->bpp = jp2k->jp2k_state.bpp;
465
3
                    jp2k->xresolution = 72;
466
3
                    jp2k->yresolution = 72;
467
3
                    jp2k->copies = 1;
468
                    /* I would have thought we should use jp2k->jp2k_state.out_numcomps,
469
                     * but this is wrong! */
470
3
                    jp2k->num_comps = jp2k->bpp/8;
471
3
                    jp2k->byte_width = (jp2k->width * jp2k->bpp + 7)>>3;
472
473
#if 0
474
                    switch (jp2k->jp2k_state.colorspace) {
475
                    default:
476
                        goto early_flush;
477
                    case gs_jpx_cs_gray:
478
                        cs = jp2k->gray;
479
                        break;
480
                    case gs_jpx_cs_rgb:
481
                        cs = jp2k->rgb;
482
                        break;
483
                    case gs_jpx_cs_cmyk:
484
                        cs = jp2k->cmyk;
485
                        break;
486
                    }
487
#else
488
3
                    switch (jp2k->num_comps) {
489
1
                    case 1:
490
1
                        cs = jp2k->gray;
491
1
                        break;
492
0
                    case 3:
493
0
                        cs = jp2k->rgb;
494
0
                        break;
495
0
                    case 4:
496
0
                        cs = jp2k->cmyk;
497
0
                        break;
498
2
                    default:
499
2
                        goto early_flush;
500
3
                    }
501
1
#endif
502
503
                    /* Scale to fit, if too large. */
504
1
                    scale = 1.0f;
505
1
                    if (jp2k->width * jp2k->dev->HWResolution[0] > jp2k->dev->width * jp2k->xresolution)
506
0
                        scale = ((float)jp2k->dev->width * jp2k->xresolution) / (jp2k->width * jp2k->dev->HWResolution[0]);
507
1
                    if (scale * jp2k->height * jp2k->dev->HWResolution[1] > jp2k->dev->height * jp2k->yresolution)
508
0
                        scale = ((float)jp2k->dev->height * jp2k->yresolution) / (jp2k->height * jp2k->dev->HWResolution[1]);
509
510
1
                    jp2k->nulldev = gs_currentdevice(jp2k->pgs);
511
1
                    rc_increment(jp2k->nulldev);
512
1
                    code = gs_setdevice_no_erase(jp2k->pgs, jp2k->dev);
513
1
                    if (code < 0)
514
0
                        goto early_flush;
515
1
                    gs_initmatrix(jp2k->pgs);
516
517
                    /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
518
1
                    xext = (((float)jp2k->width) * 72 * scale / jp2k->xresolution);
519
1
                    xoffset = (jp2k->dev->width * 72 / jp2k->dev->HWResolution[0] - xext)/2;
520
1
                    yext = (((float)jp2k->height) * 72 * scale / jp2k->yresolution);
521
1
                    yoffset = (jp2k->dev->height * 72 / jp2k->dev->HWResolution[1] - yext)/2;
522
523
                    /* By default the ctm is set to:
524
                     *   xres/72   0
525
                     *   0         -yres/72
526
                     *   0         dev->height * yres/72
527
                     * i.e. it moves the origin from being top right to being bottom left.
528
                     * We want to move it back, as without this, the image will be displayed
529
                     * upside down.
530
                     */
531
1
                    code = gs_translate(jp2k->pgs, 0.0, jp2k->dev->height * 72 / jp2k->dev->HWResolution[1]);
532
1
                    if (code >= 0)
533
1
                        code = gs_translate(jp2k->pgs, xoffset, -yoffset);
534
1
                    if (code >= 0)
535
1
                        code = gs_scale(jp2k->pgs, scale, -scale);
536
                    /* At this point, the ctm is set to:
537
                     *   xres/72  0
538
                     *   0        yres/72
539
                     *   0        0
540
                     */
541
1
                    if (code >= 0)
542
1
                        code = gs_erasepage(jp2k->pgs);
543
1
                    if (code < 0)
544
0
                        goto early_flush;
545
546
1
                    memset(&jp2k->image, 0, sizeof(jp2k->image));
547
1
                    gs_image_t_init(&jp2k->image, cs);
548
1
                    jp2k->image.BitsPerComponent = jp2k->bpp/jp2k->num_comps;
549
1
                    jp2k->image.Width = jp2k->width;
550
1
                    jp2k->image.Height = jp2k->height;
551
552
1
                    jp2k->image.ImageMatrix.xx = jp2k->xresolution / 72.0f;
553
1
                    jp2k->image.ImageMatrix.yy = jp2k->yresolution / 72.0f;
554
555
1
                    jp2k->penum = gs_image_enum_alloc(jp2k->memory, "jp2k_impl_process(penum)");
556
1
                    if (jp2k->penum == NULL) {
557
0
                        code = gs_note_error(gs_error_VMerror);
558
0
                        goto early_flush;
559
0
                    }
560
561
1
                    code = gs_image_init(jp2k->penum,
562
1
                                         &jp2k->image,
563
1
                                         false,
564
1
                                         false,
565
1
                                         jp2k->pgs);
566
1
                    if (code < 0)
567
0
                        goto early_flush;
568
1
                }
569
570
1
                if (jp2k->penum == NULL)
571
0
                    goto flush;
572
573
1
                code = gs_image_next(jp2k->penum, jp2k->stream_buffer, local_w.ptr + 1 - jp2k->stream_buffer, &used);
574
1
                if (code < 0)
575
0
                    goto flush;
576
577
1
                jp2k->x += used;
578
13
                while (jp2k->x >= jp2k->byte_width) {
579
12
                    jp2k->x -= jp2k->byte_width;
580
12
                    jp2k->y++;
581
12
                }
582
                /* Loop while we haven't had all the lines, the decompression
583
                 * didn't ask for more data, and the decompression didn't give
584
                 * us more data. */
585
1
            } while (jp2k->y < jp2k->height);
586
587
1.22k
            if (jp2k->penum && jp2k->y == jp2k->height) {
588
1
                code = gs_image_cleanup_and_free_enum(jp2k->penum, jp2k->pgs);
589
1
                jp2k->penum = NULL;
590
1
                if (code < 0)
591
0
                    goto flush;
592
1
                code = pl_finish_page(jp2k->memory->gs_lib_ctx->top_of_system,
593
1
                                      jp2k->pgs, jp2k->copies, true);
594
1
                if (code < 0)
595
0
                    goto flush;
596
1
                if (jp2k->pages > 0) {
597
0
                    jp2k->pages--;
598
                    /* If we've reached the expected end, we should probably flush to UEL */
599
0
                    if (jp2k->pages == 0)
600
0
                        jp2k->state = ii_state_flush;
601
0
                }
602
1
                if (jp2k->jp2k_state.templat->release)
603
1
                    jp2k->jp2k_state.templat->release((stream_state *)&jp2k->jp2k_state);
604
1
                jp2k->state = ii_state_jp2k_header;
605
1
            }
606
1.22k
            break;
607
1.22k
        }
608
1.22k
        default:
609
0
        case ii_state_flush:
610
0
            if (0) {
611
0
flush:
612
0
                if (jp2k->jp2k_state.templat->release)
613
0
                    jp2k->jp2k_state.templat->release((stream_state *)&jp2k->jp2k_state);
614
2
early_flush:
615
2
                jp2k->state = ii_state_flush;
616
2
            }
617
            /* We want to bin any data we get up to, but not including
618
             * a UEL. */
619
2
            return flush_to_uel(pr);
620
1.42k
        }
621
1.42k
        advanced |= (ostate != jp2k->state);
622
1.42k
        advanced |= (bytes_in != pr->limit - pr->ptr);
623
1.42k
    } while (advanced);
624
625
666
    if (code == 0)
626
666
        code = gs_note_error(gs_error_NeedInput);
627
628
666
    return code;
629
668
}
630
631
static int
632
jp2k_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
633
568
{
634
568
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
635
636
568
    return do_process(jp2k, pr, 0);
637
568
}
638
639
static int
640
jp2k_impl_process_end(pl_interp_implementation_t * impl)
641
100
{
642
100
    return 0;
643
100
}
644
645
/* Not implemented */
646
static int
647
jp2k_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
648
0
{
649
0
    const byte *p = cursor->ptr;
650
0
    const byte *rlimit = cursor->limit;
651
652
    /* Skip to, but leave UEL in buffer for PJL to find later */
653
0
    for (; p < rlimit; ++p)
654
0
        if (p[1] == '\033') {
655
0
            uint avail = rlimit - p;
656
657
0
            if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
658
0
                continue;
659
0
            if (avail < 9)
660
0
                break;
661
0
            cursor->ptr = p;
662
0
            return 1;           /* found eoj */
663
0
        }
664
0
    cursor->ptr = p;
665
0
    return 0;                   /* need more */
666
0
}
667
668
/* Parser action for end-of-file */
669
static int
670
jp2k_impl_process_eof(pl_interp_implementation_t *impl)
671
100
{
672
100
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
673
100
    stream_cursor_read cursor;
674
675
100
    cursor.ptr = NULL;
676
100
    cursor.limit = NULL;
677
678
100
    return do_process(jp2k, &cursor, 1);
679
100
}
680
681
/* Report any errors after running a job */
682
static int
683
jp2k_impl_report_errors(pl_interp_implementation_t *impl,          /* interp instance to wrap up job in */
684
                       int                         code,          /* prev termination status */
685
                       long                        file_position, /* file position of error, -1 if unknown */
686
                       bool                        force_to_cout  /* force errors to cout */
687
)
688
0
{
689
0
    return 0;
690
0
}
691
692
/* Wrap up interp instance after a "job" */
693
static int
694
jp2k_impl_dnit_job(pl_interp_implementation_t *impl)
695
100
{
696
100
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
697
698
100
    if (jp2k->nulldev) {
699
1
        int code = gs_setdevice(jp2k->pgs, jp2k->nulldev);
700
1
        jp2k->dev = NULL;
701
1
        rc_decrement(jp2k->nulldev, "jp2k_impl_dnit_job(nulldevice)");
702
1
        jp2k->nulldev = NULL;
703
1
        return code;
704
1
    }
705
99
    return 0;
706
100
}
707
708
/* Parser implementation descriptor */
709
const pl_interp_implementation_t jp2k_implementation = {
710
  jp2k_impl_characteristics,
711
  jp2k_impl_allocate_interp_instance,
712
  jp2k_impl_get_device_memory,
713
  NULL, /* jp2k_impl_set_param */
714
  NULL, /* jp2k_impl_add_path */
715
  NULL, /* jp2k_impl_post_args_init */
716
  jp2k_impl_init_job,
717
  NULL, /* jp2k_impl_run_prefix_commands */
718
  NULL, /* jp2k_impl_process_file */
719
  jp2k_impl_process_begin,
720
  jp2k_impl_process,
721
  jp2k_impl_process_end,
722
  jp2k_impl_flush_to_eoj,
723
  jp2k_impl_process_eof,
724
  jp2k_impl_report_errors,
725
  jp2k_impl_dnit_job,
726
  jp2k_impl_deallocate_interp_instance,
727
  NULL, /* jp2k_impl_reset */
728
  NULL  /* interp_client_data */
729
};