Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/gpdl/jbig2top.c
Line
Count
Source
1
/* Copyright (C) 2019-2023 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
/* jbig2top.c */
17
/* Top-level API implementation of "JBIG2" 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 "jbig2.h"
29
30
/* Forward decls */
31
32
/************************************************************/
33
/******** Language wrapper implementation (see pltop.h) *****/
34
/************************************************************/
35
36
typedef enum
37
{
38
    ii_state_identifying = 0,
39
    ii_state_jbig2,
40
    ii_state_jbig2_start,
41
    ii_state_jbig2_decode,
42
    ii_state_flush
43
} ii_state;
44
45
/*
46
 * JBig2 interpreter instance
47
 */
48
typedef struct jbig2_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
56
    /* JBig2 parser state machine */
57
    ii_state           state;
58
59
    gs_image_t         image;
60
    gs_image_enum     *penum;
61
    gs_gstate         *pgs;
62
63
    Jbig2Ctx          *jbig_ctx;
64
    struct _Jbig2Allocator allocator;
65
66
    byte              *samples;
67
68
} jbig2_interp_instance_t;
69
70
static int
71
jbig2_detect_language(const char *s, int len)
72
17.7k
{
73
17.7k
    const byte *hdr = (const byte *)s;
74
17.7k
    if (len >= 8) {
75
17.3k
        if (hdr[0] == 0x97 &&
76
10
            hdr[1] == 'J' &&
77
0
            hdr[2] == 'B' &&
78
0
            hdr[3] == '2' &&
79
0
            hdr[4] == 0x0d &&
80
0
            hdr[5] == 0x0a &&
81
0
            hdr[6] == 0x1a &&
82
0
            hdr[7] == 0x0a)
83
0
            return 100;
84
17.3k
    }
85
86
17.7k
    return 0;
87
17.7k
}
88
89
static const pl_interp_characteristics_t jbig2_characteristics = {
90
    "JBIG2",
91
    jbig2_detect_language,
92
};
93
94
/* Get implementation's characteristics */
95
static const pl_interp_characteristics_t * /* always returns a descriptor */
96
jbig2_impl_characteristics(const pl_interp_implementation_t *impl)     /* implementation of interpreter to alloc */
97
37.5k
{
98
37.5k
  return &jbig2_characteristics;
99
37.5k
}
100
101
static void
102
jbig2_deallocate(jbig2_interp_instance_t *jbig2)
103
8.09k
{
104
8.09k
    if (jbig2 == NULL)
105
0
        return;
106
107
8.09k
    rc_decrement_cs(jbig2->gray, "jbig2_deallocate");
108
109
8.09k
    if (jbig2->pgs != NULL)
110
8.09k
        gs_gstate_free_chain(jbig2->pgs);
111
8.09k
    gs_free_object(jbig2->memory, jbig2, "jbig2_impl_allocate_interp_instance");
112
8.09k
}
113
114
/* Deallocate a interpreter instance */
115
static int
116
jbig2_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
117
8.09k
{
118
8.09k
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
119
120
8.09k
    jbig2_deallocate(jbig2);
121
8.09k
    impl->interp_client_data = NULL;
122
123
8.09k
    return 0;
124
8.09k
}
125
126
/* Do per-instance interpreter allocation/init. */
127
static int
128
jbig2_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
129
8.09k
{
130
8.09k
    int code;
131
8.09k
    jbig2_interp_instance_t *jbig2
132
8.09k
        = (jbig2_interp_instance_t *)gs_alloc_bytes(mem,
133
8.09k
                                                  sizeof(jbig2_interp_instance_t),
134
8.09k
                                                  "jbig2_impl_allocate_interp_instance");
135
8.09k
    if (!jbig2)
136
0
        return_error(gs_error_VMerror);
137
8.09k
    memset(jbig2, 0, sizeof(*jbig2));
138
139
8.09k
    jbig2->memory = mem;
140
8.09k
    jbig2->pgs = gs_gstate_alloc(mem);
141
8.09k
    if (jbig2->pgs == NULL)
142
0
        goto failVM;
143
144
    /* Push one save level onto the stack to assuage the memory handling */
145
8.09k
    code = gs_gsave(jbig2->pgs);
146
8.09k
    if (code < 0)
147
0
        goto fail;
148
149
8.09k
    code = gsicc_init_iccmanager(jbig2->pgs);
150
8.09k
    if (code < 0)
151
0
        goto fail;
152
153
8.09k
    jbig2->gray = gs_cspace_new_ICC(mem, jbig2->pgs, 1);
154
155
8.09k
    impl->interp_client_data = jbig2;
156
157
8.09k
    return 0;
158
159
0
failVM:
160
0
    code = gs_note_error(gs_error_VMerror);
161
0
fail:
162
0
    (void)jbig2_deallocate(jbig2);
163
0
    return code;
164
0
}
165
166
/*
167
 * Get the allocator with which to allocate a device
168
 */
169
static gs_memory_t *
170
jbig2_impl_get_device_memory(pl_interp_implementation_t *impl)
171
0
{
172
0
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
173
174
0
    return jbig2->dev ? jbig2->dev->memory : NULL;
175
0
}
176
177
/* Prepare interp instance for the next "job" */
178
static int
179
jbig2_impl_init_job(pl_interp_implementation_t *impl,
180
                  gx_device                  *device)
181
0
{
182
0
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
183
184
0
    jbig2->dev = device;
185
0
    jbig2->state = ii_state_identifying;
186
187
0
    return 0;
188
0
}
189
190
/* Do any setup for parser per-cursor */
191
static int                      /* ret 0 or +ve if ok, else -ve error code */
192
jbig2_impl_process_begin(pl_interp_implementation_t * impl)
193
0
{
194
0
    return 0;
195
0
}
196
197
/* Ensure we have 'required' bytes to read, and further ensure
198
 * that we have no UEL's within those bytes. */
199
static int
200
ensure_bytes(jbig2_interp_instance_t *jpg, stream_cursor_read *pr, int required)
201
0
{
202
0
    int n;
203
0
    const uint8_t *p = pr->ptr+1;
204
0
    const uint8_t *q;
205
0
    int avail;
206
207
    /* Find out how many bytes we need to check */
208
0
    n = pr->limit - pr->ptr;
209
0
    if (n > required)
210
0
        n = required;
211
212
    /* Make sure there are no UELs in that block */
213
0
    q = p + n;
214
0
    while (p != q) {
215
0
        while (p != q && *p != '\033')
216
0
            p++;
217
0
        if (p == q)
218
0
            break;
219
0
        avail = pr->limit - pr->ptr;
220
0
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
221
            /* At least a partial match to a UEL */
222
0
            return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit;
223
0
        }
224
0
        p++;
225
0
    }
226
227
    /* If we have enough bytes, great, if not, get some more */
228
0
    return (n < required) ? gs_error_NeedInput : 0;
229
0
}
230
231
static int
232
flush_to_uel(stream_cursor_read *pr)
233
0
{
234
0
    const uint8_t *p = pr->ptr+1;
235
0
    const uint8_t *q = pr->limit+1;
236
0
    int avail;
237
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. Bin everything to
246
             * the start of the match. */
247
0
            pr->ptr = p-1;
248
0
            if (avail == 9) /* Complete match. Exit! */
249
0
                return gs_error_InterpreterExit;
250
            /* Partial match. Get more data. */
251
0
            return gs_error_NeedInput;
252
0
        }
253
0
        p++;
254
0
    }
255
256
0
    pr->ptr = pr->limit;
257
258
0
    return 0;
259
0
}
260
261
static int
262
bytes_until_uel(const stream_cursor_read *pr)
263
0
{
264
0
    const uint8_t *p = pr->ptr+1;
265
0
    const uint8_t *q = pr->limit+1;
266
0
    int avail;
267
268
0
    while (p != q) {
269
0
        while (p != q && *p != '\033')
270
0
            p++;
271
0
        if (p == q)
272
0
            break;
273
0
        avail = q - p;
274
0
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
275
            /* At least a partial match to a UEL. Everything up to
276
             * the start of the match is up for grabs. */
277
0
            return p - (pr->ptr+1);
278
0
        }
279
0
        p++;
280
0
    }
281
282
0
    return pr->limit - pr->ptr;
283
0
}
284
285
static void my_errors(void *data, const char *msg, Jbig2Severity severity, uint32_t seg_idx)
286
0
{
287
    /* Do nothing */
288
0
}
289
290
static void *my_alloc(Jbig2Allocator *allocator, size_t size)
291
0
{
292
0
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)(((char *)allocator)-offsetof(jbig2_interp_instance_t, allocator));
293
294
0
    return gs_alloc_bytes(jbig2->memory, size, "jbig2(my_alloc)");
295
0
}
296
297
static void my_free(Jbig2Allocator *allocator, void *p)
298
0
{
299
0
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)(((char *)allocator)-offsetof(jbig2_interp_instance_t, allocator));
300
301
0
    gs_free_object(jbig2->memory, p, "jbig2(my_free)");
302
0
}
303
304
static void *my_realloc(Jbig2Allocator *allocator, void *p, size_t size)
305
0
{
306
0
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)(((char *)allocator)-offsetof(jbig2_interp_instance_t, allocator));
307
308
0
    return gs_resize_object(jbig2->memory, p, size, "jbig2(my_realloc)");
309
0
}
310
311
static int
312
do_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr, int eof)
313
0
{
314
0
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
315
0
    int code = 0;
316
0
    ii_state ostate = (ii_state)-1;
317
0
    int bytes_left = 0;
318
319
0
    while (jbig2->state != ostate || pr->limit - pr->ptr != bytes_left)
320
0
    {
321
0
        ostate = jbig2->state;
322
0
        bytes_left = pr->limit - pr->ptr;
323
0
        switch(jbig2->state)
324
0
        {
325
0
        case ii_state_identifying:
326
0
        {
327
0
            const byte *hdr;
328
            /* Try and get us 8 bytes */
329
0
            code = ensure_bytes(jbig2, pr, 8);
330
0
            if (code < 0)
331
0
                return code;
332
0
            hdr = pr->ptr+1;
333
0
            if (hdr[0] == 0x97 &&
334
0
                hdr[1] == 'J' &&
335
0
                hdr[2] == 'B' &&
336
0
                hdr[3] == '2' &&
337
0
                hdr[4] == 0x0d &&
338
0
                hdr[5] == 0x0a &&
339
0
                hdr[6] == 0x1a &&
340
0
                hdr[7] == 0x0a) {
341
0
                jbig2->state = ii_state_jbig2;
342
0
                break;
343
0
            }
344
0
            jbig2->state = ii_state_flush;
345
0
            break;
346
0
        }
347
0
        case ii_state_jbig2:
348
0
        {
349
            /* Gather data into a buffer */
350
0
            int bytes = bytes_until_uel(pr);
351
352
0
            if (bytes == 0 && pr->limit - pr->ptr > 9) {
353
                /* No bytes until UEL, and there is space for a UEL in the buffer */
354
0
                jbig2->state = ii_state_jbig2_start;
355
0
                break;
356
0
            }
357
0
            if (bytes == 0 && eof) {
358
                /* No bytes until UEL, and we are at eof */
359
0
                jbig2->state = ii_state_jbig2_start;
360
0
                break;
361
0
            }
362
363
0
            if (jbig2->jbig_ctx == NULL) {
364
0
                jbig2->allocator.alloc = &my_alloc;
365
0
                jbig2->allocator.free = &my_free;
366
0
                jbig2->allocator.realloc = &my_realloc;
367
0
                jbig2->jbig_ctx = jbig2_ctx_new(&jbig2->allocator,
368
0
                                                0, /* Options */
369
0
                                                NULL, /* Global ctx */
370
0
                                                &my_errors,
371
0
                                                jbig2);
372
0
                if (jbig2->jbig_ctx == NULL) {
373
0
                    jbig2->state = ii_state_flush;
374
0
                    break;
375
0
                }
376
0
            }
377
0
            if (jbig2_data_in(jbig2->jbig_ctx, pr->ptr+1, bytes)) {
378
0
                jbig2->state = ii_state_flush;
379
0
                break;
380
0
            }
381
0
            pr->ptr += bytes;
382
0
            break;
383
0
        }
384
0
        case ii_state_jbig2_start:
385
            /* This state exists so we can change back to it after
386
             * a successful decode. It avoids the enclosing loop
387
             * exiting after the first image of a jbig2 due to the
388
             * state not having changed. We could avoid this by using
389
             * a while loop in the "decode" state below, but that would
390
             * make breaking harder. */
391
0
            jbig2->state = ii_state_jbig2_decode;
392
0
            break;
393
0
        case ii_state_jbig2_decode:
394
0
        {
395
0
            float xext, yext, xoffset, yoffset, scale;
396
0
            unsigned long y, w, h;
397
0
            unsigned int used;
398
0
            Jbig2Image *img = jbig2_page_out(jbig2->jbig_ctx);
399
0
            if (img == NULL) {
400
0
                jbig2->state = ii_state_flush;
401
0
                break;
402
0
            }
403
0
            w = img->width;
404
0
            h = img->height;
405
406
            /* Scale to fit, if too large. */
407
0
            scale = 1.0f;
408
0
            if (w * jbig2->dev->HWResolution[0] > jbig2->dev->width * 200)
409
0
                scale = ((float)jbig2->dev->width * 200) / (w * jbig2->dev->HWResolution[0]);
410
0
            if (scale * h * jbig2->dev->HWResolution[1] > jbig2->dev->height * 200)
411
0
                scale = ((float)jbig2->dev->height * 200) / (h * jbig2->dev->HWResolution[1]);
412
413
0
            jbig2->nulldev = gs_currentdevice(jbig2->pgs);
414
0
            rc_increment(jbig2->nulldev);
415
0
            code = gs_setdevice_no_erase(jbig2->pgs, jbig2->dev);
416
0
            if (code < 0)
417
0
                goto fail_during_decode;
418
419
0
            code = gs_erasepage(jbig2->pgs);
420
0
            if (code < 0)
421
0
                goto fail_during_decode;
422
423
0
            jbig2->penum = gs_image_enum_alloc(jbig2->memory, "jbig2_impl_process(penum)");
424
0
            if (jbig2->penum == NULL) {
425
0
                code = gs_note_error(gs_error_VMerror);
426
0
                goto fail_during_decode;
427
0
            }
428
429
            /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
430
0
            xext = (((float)w) * 72 * scale / 200);
431
0
            xoffset = (jbig2->dev->width * 72 / jbig2->dev->HWResolution[0] - xext)/2;
432
0
            yext = (((float)h) * 72 * scale / 200);
433
0
            yoffset = (jbig2->dev->height * 72 / jbig2->dev->HWResolution[1] - yext)/2;
434
435
0
            gs_initmatrix(jbig2->pgs);
436
437
            /* By default the ctm is set to:
438
             *   xres/72   0
439
             *   0         -yres/72
440
             *   0         dev->height * yres/72
441
             * i.e. it moves the origin from being top right to being bottom left.
442
             * We want to move it back, as without this, the image will be displayed
443
             * upside down.
444
             */
445
0
            code = gs_translate(jbig2->pgs, 0.0, jbig2->dev->height * 72 / jbig2->dev->HWResolution[1]);
446
0
            if (code >= 0)
447
0
                code = gs_translate(jbig2->pgs, xoffset, -yoffset);
448
0
            if (code >= 0)
449
0
                code = gs_scale(jbig2->pgs, scale, -scale);
450
0
            if (code < 0)
451
0
                goto fail_during_decode;
452
453
0
            memset(&jbig2->image, 0, sizeof(jbig2->image));
454
0
            gs_image_t_init(&jbig2->image, jbig2->gray);
455
0
            jbig2->image.BitsPerComponent = 1;
456
0
            jbig2->image.Width = w;
457
0
            jbig2->image.Height = h;
458
459
0
            jbig2->image.ImageMatrix.xx = 200.0f/72.0f;
460
0
            jbig2->image.ImageMatrix.yy = 200.0f/72.0f;
461
0
            jbig2->image.Decode[0] = 1.0f;
462
0
            jbig2->image.Decode[1] = 0.0f;
463
464
0
            code = gs_image_init(jbig2->penum,
465
0
                                 &jbig2->image,
466
0
                                 false,
467
0
                                 false,
468
0
                                 jbig2->pgs);
469
0
            if (code < 0)
470
0
                goto fail_during_decode;
471
472
0
            for (y = 0; y < img->height; y++) {
473
0
                code = gs_image_next(jbig2->penum,
474
0
                                     &img->data[y*img->stride],
475
0
                                     (img->width+7)>>3,
476
0
                                     &used);
477
0
                if (code < 0)
478
0
                    goto fail_during_decode;
479
0
            }
480
0
            jbig2_release_page(jbig2->jbig_ctx, img);
481
0
            code = gs_image_cleanup_and_free_enum(jbig2->penum, jbig2->pgs);
482
0
            jbig2->penum = NULL;
483
0
            if (code < 0) {
484
0
                jbig2->state = ii_state_flush;
485
0
                break;
486
0
            }
487
0
            (void)pl_finish_page(jbig2->memory->gs_lib_ctx->top_of_system,
488
0
                                 jbig2->pgs, 1, true);
489
0
            jbig2->state = ii_state_jbig2_start;
490
0
            break;
491
0
fail_during_decode:
492
0
            jbig2_release_page(jbig2->jbig_ctx, img);
493
0
            jbig2->state = ii_state_flush;
494
0
            break;
495
0
        }
496
0
        default:
497
0
        case ii_state_flush:
498
0
            if (jbig2->jbig_ctx) {
499
0
                jbig2_ctx_free(jbig2->jbig_ctx);
500
0
                jbig2->jbig_ctx = NULL;
501
0
            }
502
503
0
            if (jbig2->penum) {
504
0
                (void)gs_image_cleanup_and_free_enum(jbig2->penum, jbig2->pgs);
505
0
                jbig2->penum = NULL;
506
0
            }
507
508
            /* We want to bin any data we get up to, but not including
509
             * a UEL. */
510
0
            return flush_to_uel(pr);
511
0
        }
512
0
    }
513
514
0
    return code;
515
0
}
516
517
static int
518
0
jbig2_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr) {
519
0
    return do_impl_process(impl, pr, 0);
520
0
}
521
522
static int
523
jbig2_impl_process_end(pl_interp_implementation_t * impl)
524
0
{
525
0
    return 0;
526
0
}
527
528
/* Not implemented */
529
static int
530
jbig2_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
531
0
{
532
0
    const byte *p = cursor->ptr;
533
0
    const byte *rlimit = cursor->limit;
534
535
    /* Skip to, but leave UEL in buffer for PJL to find later */
536
0
    for (; p < rlimit; ++p)
537
0
        if (p[1] == '\033') {
538
0
            uint avail = rlimit - p;
539
540
0
            if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
541
0
                continue;
542
0
            if (avail < 9)
543
0
                break;
544
0
            cursor->ptr = p;
545
0
            return 1;           /* found eoj */
546
0
        }
547
0
    cursor->ptr = p;
548
0
    return 0;                   /* need more */
549
0
}
550
551
/* Parser action for end-of-file */
552
static int
553
jbig2_impl_process_eof(pl_interp_implementation_t *impl)
554
0
{
555
0
    stream_cursor_read r;
556
557
0
    r.ptr = NULL;
558
0
    r.limit = NULL;
559
0
    return do_impl_process(impl, &r, 1);
560
0
}
561
562
/* Report any errors after running a job */
563
static int
564
jbig2_impl_report_errors(pl_interp_implementation_t *impl,          /* interp instance to wrap up job in */
565
                        int                         code,          /* prev termination status */
566
                        long                        file_position, /* file position of error, -1 if unknown */
567
                        bool                        force_to_cout  /* force errors to cout */
568
)
569
0
{
570
0
    return 0;
571
0
}
572
573
/* Wrap up interp instance after a "job" */
574
static int
575
jbig2_impl_dnit_job(pl_interp_implementation_t *impl)
576
0
{
577
0
    jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
578
579
0
    if (jbig2->nulldev) {
580
0
        int code = gs_setdevice(jbig2->pgs, jbig2->nulldev);
581
0
        jbig2->dev = NULL;
582
0
        rc_decrement(jbig2->nulldev, "jbig2_impl_dnit_job(nulldevice)");
583
0
        jbig2->nulldev = NULL;
584
0
        return code;
585
0
    }
586
0
    return 0;
587
0
}
588
589
/* Parser implementation descriptor */
590
const pl_interp_implementation_t jbig2_implementation = {
591
  jbig2_impl_characteristics,
592
  jbig2_impl_allocate_interp_instance,
593
  jbig2_impl_get_device_memory,
594
  NULL, /* jbig2_impl_set_param */
595
  NULL, /* jbig2_impl_add_path */
596
  NULL, /* jbig2_impl_post_args_init */
597
  jbig2_impl_init_job,
598
  NULL, /* jbig2_impl_run_prefix_commands */
599
  NULL, /* jbig2_impl_process_file */
600
  jbig2_impl_process_begin,
601
  jbig2_impl_process,
602
  jbig2_impl_process_end,
603
  jbig2_impl_flush_to_eoj,
604
  jbig2_impl_process_eof,
605
  jbig2_impl_report_errors,
606
  jbig2_impl_dnit_job,
607
  jbig2_impl_deallocate_interp_instance,
608
  NULL, /* jbig2_impl_reset */
609
  NULL  /* interp_client_data */
610
};