Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/gpdl/jp2ktop.c
Line
Count
Source
1
/* Copyright (C) 2019-2024 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
17.7k
{
87
17.7k
    const unsigned char *s = (const unsigned char *)s_;
88
17.7k
    if (len >= 12) {
89
17.1k
        if (s[0] == 0x00 &&
90
756
            s[1] == 0x00 &&
91
254
            s[2] == 0x00 &&
92
212
            s[3] == 0x0C &&
93
2
            s[4] == 0x6a &&
94
0
            s[5] == 0x50 &&
95
0
            ((s[6] == 0x1a && s[7] == 0x1a) || (s[6] == 0x20 && s[7] == 0x20)) &&
96
0
            s[8] == 0x0d &&
97
0
            s[9] == 0x0a &&
98
0
            s[10] == 0x87 &&
99
0
            s[11] == 0x0a)
100
0
            return 100; /* JP2 file */
101
17.1k
    }
102
17.7k
    if (len >= 4) {
103
17.7k
        if (s[0] == 0xff &&
104
193
            s[1] == 0x4f &&
105
22
            s[2] == 0xff &&
106
20
            s[3] == 0x51)
107
20
            return 100; /* J2K stream */
108
17.7k
    }
109
110
17.7k
    return 0;
111
17.7k
}
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
37.5k
{
122
37.5k
  return &jp2k_characteristics;
123
37.5k
}
124
125
static void
126
jp2k_deallocate(jp2k_interp_instance_t *jp2k)
127
8.09k
{
128
8.09k
    if (jp2k == NULL)
129
0
        return;
130
131
8.09k
    rc_decrement_cs(jp2k->gray, "jp2k_deallocate");
132
8.09k
    rc_decrement_cs(jp2k->rgb, "jp2k_deallocate");
133
8.09k
    rc_decrement_cs(jp2k->cmyk, "jp2k_deallocate");
134
135
8.09k
    if (jp2k->pgs != NULL)
136
8.09k
        gs_gstate_free_chain(jp2k->pgs);
137
8.09k
    gs_free_object(jp2k->memory, jp2k, "jp2k_impl_allocate_interp_instance");
138
8.09k
}
139
140
/* Deallocate a interpreter instance */
141
static int
142
jp2k_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
143
8.09k
{
144
8.09k
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
145
146
8.09k
    jp2k_deallocate(jp2k);
147
8.09k
    impl->interp_client_data = NULL;
148
149
8.09k
    return 0;
150
8.09k
}
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
8.09k
{
156
8.09k
    int code;
157
8.09k
    jp2k_interp_instance_t *jp2k
158
8.09k
        = (jp2k_interp_instance_t *)gs_alloc_bytes(mem,
159
8.09k
                                                  sizeof(jp2k_interp_instance_t),
160
8.09k
                                                  "jp2k_impl_allocate_interp_instance");
161
8.09k
    if (!jp2k)
162
0
        return_error(gs_error_VMerror);
163
8.09k
    memset(jp2k, 0, sizeof(*jp2k));
164
165
8.09k
    jp2k->memory = mem;
166
8.09k
    jp2k->pgs = gs_gstate_alloc(mem);
167
8.09k
    if (jp2k->pgs == NULL)
168
0
        goto failVM;
169
170
    /* Push one save level onto the stack to assuage the memory handling */
171
8.09k
    code = gs_gsave(jp2k->pgs);
172
8.09k
    if (code < 0)
173
0
        goto fail;
174
175
8.09k
    code = gsicc_init_iccmanager(jp2k->pgs);
176
8.09k
    if (code < 0)
177
0
        goto fail;
178
179
8.09k
    jp2k->gray = gs_cspace_new_ICC(mem, jp2k->pgs, 1);
180
8.09k
    jp2k->rgb  = gs_cspace_new_ICC(mem, jp2k->pgs, 3);
181
8.09k
    jp2k->cmyk = gs_cspace_new_ICC(mem, jp2k->pgs, 4);
182
183
8.09k
    impl->interp_client_data = jp2k;
184
185
8.09k
    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
20
{
243
20
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
244
245
20
    jp2k->dev = device;
246
20
    jp2k->state = ii_state_identifying;
247
248
20
    return 0;
249
20
}
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
20
{
274
20
    return 0;
275
20
}
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
20
{
282
20
    int n;
283
20
    const uint8_t *p = pr->ptr+1;
284
20
    const uint8_t *q;
285
20
    int avail;
286
287
    /* Find out how many bytes we need to check */
288
20
    n = pr->limit - pr->ptr;
289
20
    if (n > required)
290
19
        n = required;
291
292
    /* Make sure there are no UELs in that block */
293
20
    q = p + n;
294
20
    while (p != q) {
295
100
        while (p != q && *p != '\033')
296
80
            p++;
297
20
        if (p == q)
298
20
            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
20
    return (n < required) ? gs_error_NeedInput : 0;
309
20
}
310
311
static int
312
flush_to_uel(stream_cursor_read *pr)
313
0
{
314
0
    const uint8_t *p = pr->ptr+1;
315
0
    const uint8_t *q = pr->limit+1;
316
0
    int avail;
317
318
0
    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
0
    pr->ptr = pr->limit;
337
338
0
    return 0;
339
0
}
340
341
static int
342
bytes_until_uel(const stream_cursor_read *pr)
343
418
{
344
418
    const uint8_t *p = pr->ptr+1;
345
418
    const uint8_t *q = pr->limit+1;
346
418
    int avail;
347
348
555
    while (p != q) {
349
381k
        while (p != q && *p != '\033')
350
381k
            p++;
351
333
        if (p == q)
352
196
            break;
353
137
        avail = q - p;
354
137
        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
0
            return p - (pr->ptr+1);
358
0
        }
359
137
        p++;
360
137
    }
361
362
418
    return pr->limit - pr->ptr;
363
418
}
364
365
static int
366
do_process(jp2k_interp_instance_t *jp2k, stream_cursor_read * pr, bool eof)
367
222
{
368
222
    int code = 0;
369
222
    gs_color_space *cs;
370
222
    ii_state ostate;
371
222
    size_t bytes_in;
372
222
    int advanced;
373
374
    /* Loop until we stop 'advancing'. */
375
222
    do
376
458
    {
377
458
        advanced = 0;
378
458
        ostate = jp2k->state;
379
458
        bytes_in = pr->limit - pr->ptr;
380
458
        switch(jp2k->state)
381
458
        {
382
20
        case ii_state_identifying:
383
20
        {
384
20
            const byte *s = pr->ptr+1;
385
386
20
            code = ensure_bytes(jp2k, pr, 4);
387
20
            if (code < 0)
388
0
                return code;
389
20
            if (s[0] == 0xff &&
390
20
                s[1] == 0x4f &&
391
20
                s[2] == 0xff &&
392
20
                s[3] == 0x51) {
393
20
                jp2k->state = ii_state_jp2k_header;
394
20
                break;
395
20
            }
396
397
0
            code = ensure_bytes(jp2k, pr, 12);
398
0
            if (code < 0)
399
0
                return code;
400
0
            if (s[0] == 0x00 &&
401
0
                s[1] == 0x00 &&
402
0
                s[2] == 0x00 &&
403
0
                s[3] == 0x0C &&
404
0
                s[4] == 0x6a &&
405
0
                s[5] == 0x50 &&
406
0
                ((s[6] == 0x1a && s[7] == 0x1a) || (s[6] == 0x20 && s[7] == 0x20)) &&
407
0
                s[8] == 0x0d &&
408
0
                s[9] == 0x0a &&
409
0
                s[10] == 0x87 &&
410
0
                s[11] == 0x0a) {
411
0
                jp2k->state = ii_state_jp2k_header;
412
0
                break;
413
0
            }
414
0
            jp2k->state = ii_state_flush;
415
0
            break;
416
0
        }
417
20
        case ii_state_jp2k_header:
418
20
            s_init_state((stream_state *)&jp2k->jp2k_state, &s_jpxd_template, jp2k->memory);
419
20
            if (s_jpxd_template.set_defaults)
420
20
                s_jpxd_template.set_defaults((stream_state *)&jp2k->jp2k_state);
421
422
20
            code = (s_jpxd_template.init)((stream_state *)&jp2k->jp2k_state);
423
20
            if (code < 0)
424
0
                goto early_flush;
425
20
            jp2k->state = ii_state_jp2k_data;
426
20
            break;
427
418
        case ii_state_jp2k_data:
428
418
        {
429
418
            int n = bytes_until_uel(pr);
430
418
            stream_cursor_read local_r;
431
418
            stream_cursor_write local_w;
432
418
            int status;
433
418
            unsigned int used;
434
435
418
            local_r.ptr = pr->ptr;
436
418
            local_r.limit = local_r.ptr + n;
437
438
418
            do {
439
418
                local_w.ptr = jp2k->stream_buffer-1;
440
418
                local_w.limit = local_w.ptr + sizeof(jp2k->stream_buffer);
441
442
418
                status = (s_jpxd_template.process)((stream_state *)&jp2k->jp2k_state,
443
418
                                                                   &local_r, &local_w,
444
418
                                                                   eof);
445
                /* status = 0 => need data
446
                 *          1 => need output space
447
                 * but we don't actually use this currently. */
448
418
                (void)status;
449
                /* Copy the updated pointer back */
450
418
                pr->ptr = local_r.ptr;
451
418
                if (local_w.ptr + 1 == jp2k->stream_buffer)
452
418
                    break; /* Failed to decode any data */
453
454
0
                if (jp2k->width == 0) {
455
0
                    float scale, xoffset, yoffset, xext, yext;
456
457
                    /* First data we decoded! */
458
0
                    jp2k->width = jp2k->jp2k_state.width;
459
0
                    if (jp2k->width == 0) {
460
0
                        jp2k->state = ii_state_flush;
461
0
                        break;
462
0
                    }
463
0
                    jp2k->height = jp2k->jp2k_state.height;
464
0
                    jp2k->bpp = jp2k->jp2k_state.bpp;
465
0
                    jp2k->xresolution = 72;
466
0
                    jp2k->yresolution = 72;
467
0
                    jp2k->copies = 1;
468
                    /* I would have thought we should use jp2k->jp2k_state.out_numcomps,
469
                     * but this is wrong! */
470
0
                    jp2k->num_comps = jp2k->bpp/8;
471
0
                    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
0
                    switch (jp2k->num_comps) {
489
0
                    case 1:
490
0
                        cs = jp2k->gray;
491
0
                        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
0
                    default:
499
0
                        goto early_flush;
500
0
                    }
501
0
#endif
502
503
                    /* Scale to fit, if too large. */
504
0
                    scale = 1.0f;
505
0
                    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
0
                    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
0
                    jp2k->nulldev = gs_currentdevice(jp2k->pgs);
511
0
                    rc_increment(jp2k->nulldev);
512
0
                    code = gs_setdevice_no_erase(jp2k->pgs, jp2k->dev);
513
0
                    if (code < 0)
514
0
                        goto early_flush;
515
0
                    gs_initmatrix(jp2k->pgs);
516
517
                    /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
518
0
                    xext = (((float)jp2k->width) * 72 * scale / jp2k->xresolution);
519
0
                    xoffset = (jp2k->dev->width * 72 / jp2k->dev->HWResolution[0] - xext)/2;
520
0
                    yext = (((float)jp2k->height) * 72 * scale / jp2k->yresolution);
521
0
                    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
0
                    code = gs_translate(jp2k->pgs, 0.0, jp2k->dev->height * 72 / jp2k->dev->HWResolution[1]);
532
0
                    if (code >= 0)
533
0
                        code = gs_translate(jp2k->pgs, xoffset, -yoffset);
534
0
                    if (code >= 0)
535
0
                        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
0
                    if (code >= 0)
542
0
                        code = gs_erasepage(jp2k->pgs);
543
0
                    if (code < 0)
544
0
                        goto early_flush;
545
546
0
                    memset(&jp2k->image, 0, sizeof(jp2k->image));
547
0
                    gs_image_t_init(&jp2k->image, cs);
548
0
                    jp2k->image.BitsPerComponent = jp2k->bpp/jp2k->num_comps;
549
0
                    jp2k->image.Width = jp2k->width;
550
0
                    jp2k->image.Height = jp2k->height;
551
552
0
                    jp2k->image.ImageMatrix.xx = jp2k->xresolution / 72.0f;
553
0
                    jp2k->image.ImageMatrix.yy = jp2k->yresolution / 72.0f;
554
555
0
                    jp2k->penum = gs_image_enum_alloc(jp2k->memory, "jp2k_impl_process(penum)");
556
0
                    if (jp2k->penum == NULL) {
557
0
                        code = gs_note_error(gs_error_VMerror);
558
0
                        goto early_flush;
559
0
                    }
560
561
0
                    code = gs_image_init(jp2k->penum,
562
0
                                         &jp2k->image,
563
0
                                         false,
564
0
                                         false,
565
0
                                         jp2k->pgs);
566
0
                    if (code < 0)
567
0
                        goto early_flush;
568
0
                }
569
570
0
                if (jp2k->penum == NULL)
571
0
                    goto flush;
572
573
0
                code = gs_image_next(jp2k->penum, jp2k->stream_buffer, local_w.ptr + 1 - jp2k->stream_buffer, &used);
574
0
                if (code < 0)
575
0
                    goto flush;
576
577
0
                jp2k->x += used;
578
0
                while (jp2k->x >= jp2k->byte_width) {
579
0
                    jp2k->x -= jp2k->byte_width;
580
0
                    jp2k->y++;
581
0
                }
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
0
            } while (jp2k->y < jp2k->height);
586
587
418
            if (jp2k->penum && jp2k->y == jp2k->height) {
588
0
                code = gs_image_cleanup_and_free_enum(jp2k->penum, jp2k->pgs);
589
0
                jp2k->penum = NULL;
590
0
                if (code < 0)
591
0
                    goto flush;
592
0
                code = pl_finish_page(jp2k->memory->gs_lib_ctx->top_of_system,
593
0
                                      jp2k->pgs, jp2k->copies, true);
594
0
                if (code < 0)
595
0
                    goto flush;
596
0
                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
0
                if (jp2k->jp2k_state.templat->release)
603
0
                    jp2k->jp2k_state.templat->release((stream_state *)&jp2k->jp2k_state);
604
0
                jp2k->state = ii_state_jp2k_header;
605
0
            }
606
418
            break;
607
418
        }
608
418
        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
0
early_flush:
615
0
                jp2k->state = ii_state_flush;
616
0
            }
617
            /* We want to bin any data we get up to, but not including
618
             * a UEL. */
619
0
            return flush_to_uel(pr);
620
458
        }
621
458
        advanced |= (ostate != jp2k->state);
622
458
        advanced |= (bytes_in != pr->limit - pr->ptr);
623
458
    } while (advanced);
624
625
222
    return code;
626
222
}
627
628
static int
629
jp2k_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
630
202
{
631
202
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
632
633
202
    return do_process(jp2k, pr, 0);
634
202
}
635
636
static int
637
jp2k_impl_process_end(pl_interp_implementation_t * impl)
638
20
{
639
20
    return 0;
640
20
}
641
642
/* Not implemented */
643
static int
644
jp2k_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
645
0
{
646
0
    const byte *p = cursor->ptr;
647
0
    const byte *rlimit = cursor->limit;
648
649
    /* Skip to, but leave UEL in buffer for PJL to find later */
650
0
    for (; p < rlimit; ++p)
651
0
        if (p[1] == '\033') {
652
0
            uint avail = rlimit - p;
653
654
0
            if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
655
0
                continue;
656
0
            if (avail < 9)
657
0
                break;
658
0
            cursor->ptr = p;
659
0
            return 1;           /* found eoj */
660
0
        }
661
0
    cursor->ptr = p;
662
0
    return 0;                   /* need more */
663
0
}
664
665
/* Parser action for end-of-file */
666
static int
667
jp2k_impl_process_eof(pl_interp_implementation_t *impl)
668
20
{
669
20
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
670
20
    stream_cursor_read cursor;
671
672
20
    cursor.ptr = NULL;
673
20
    cursor.limit = NULL;
674
675
20
    return do_process(jp2k, &cursor, 1);
676
20
}
677
678
/* Report any errors after running a job */
679
static int
680
jp2k_impl_report_errors(pl_interp_implementation_t *impl,          /* interp instance to wrap up job in */
681
                       int                         code,          /* prev termination status */
682
                       long                        file_position, /* file position of error, -1 if unknown */
683
                       bool                        force_to_cout  /* force errors to cout */
684
)
685
0
{
686
0
    return 0;
687
0
}
688
689
/* Wrap up interp instance after a "job" */
690
static int
691
jp2k_impl_dnit_job(pl_interp_implementation_t *impl)
692
20
{
693
20
    jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
694
695
20
    if (jp2k->nulldev) {
696
0
        int code = gs_setdevice(jp2k->pgs, jp2k->nulldev);
697
0
        jp2k->dev = NULL;
698
0
        rc_decrement(jp2k->nulldev, "jp2k_impl_dnit_job(nulldevice)");
699
0
        jp2k->nulldev = NULL;
700
0
        return code;
701
0
    }
702
20
    return 0;
703
20
}
704
705
/* Parser implementation descriptor */
706
const pl_interp_implementation_t jp2k_implementation = {
707
  jp2k_impl_characteristics,
708
  jp2k_impl_allocate_interp_instance,
709
  jp2k_impl_get_device_memory,
710
  NULL, /* jp2k_impl_set_param */
711
  NULL, /* jp2k_impl_add_path */
712
  NULL, /* jp2k_impl_post_args_init */
713
  jp2k_impl_init_job,
714
  NULL, /* jp2k_impl_run_prefix_commands */
715
  NULL, /* jp2k_impl_process_file */
716
  jp2k_impl_process_begin,
717
  jp2k_impl_process,
718
  jp2k_impl_process_end,
719
  jp2k_impl_flush_to_eoj,
720
  jp2k_impl_process_eof,
721
  jp2k_impl_report_errors,
722
  jp2k_impl_dnit_job,
723
  jp2k_impl_deallocate_interp_instance,
724
  NULL, /* jp2k_impl_reset */
725
  NULL  /* interp_client_data */
726
};