Coverage Report

Created: 2026-04-01 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/coders/jbig.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                        JJJJJ  BBBB   IIIII   GGGG                           %
15
%                          J    B   B    I    G                               %
16
%                          J    BBBB     I    G  GG                           %
17
%                        J J    B   B    I    G   G                           %
18
%                        JJJ    BBBB   IIIII   GGG                            %
19
%                                                                             %
20
%                                                                             %
21
%                       Read/Write JBIG Image Format.                         %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1992                                   %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%
33
*/
34

35
/*
36
  Include declarations.
37
*/
38
#include "magick/studio.h"
39
#include "magick/blob.h"
40
#include "magick/colormap.h"
41
#include "magick/constitute.h"
42
#include "magick/log.h"
43
#include "magick/magick.h"
44
#include "magick/monitor.h"
45
#include "magick/pixel_cache.h"
46
#include "magick/resource.h"
47
#include "magick/utility.h"
48
#include "magick/static.h"
49

50
/*
51
  Forward declarations.
52
*/
53
#if defined(HasJBIG)
54
static unsigned int
55
  WriteJBIGImage(const ImageInfo *,Image *);
56
#endif
57

58
#if defined(HasJBIG)
59
#if defined(__cplusplus) || defined(c_plusplus)
60
extern "C" {
61
#endif
62
#include "jbig.h"
63
#if defined(__cplusplus) || defined(c_plusplus)
64
}
65
#endif
66
/*
67
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68
%                                                                             %
69
%                                                                             %
70
%                                                                             %
71
%   R e a d J B I G I m a g e                                                 %
72
%                                                                             %
73
%                                                                             %
74
%                                                                             %
75
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76
%
77
%  Method ReadJBIGImage reads a JBIG image file and returns it.  It
78
%  allocates the memory necessary for the new Image structure and returns a
79
%  pointer to the new image.
80
%
81
%  The format of the ReadJBIGImage method is:
82
%
83
%      Image *ReadJBIGImage(const ImageInfo *image_info,
84
%        ExceptionInfo *exception)
85
%
86
%  A description of each parameter follows:
87
%
88
%    o image:  Method ReadJBIGImage returns a pointer to the image after
89
%      reading.  A null image is returned if there is a memory shortage or
90
%      if the image cannot be read.
91
%
92
%    o image_info: Specifies a pointer to a ImageInfo structure.
93
%
94
%    o exception: return any errors or warnings in this structure.
95
%
96
%
97
*/
98
static Image *ReadJBIGImage(const ImageInfo *image_info,
99
                            ExceptionInfo *exception)
100
2.88k
{
101
9.04k
#define MaxBufferSize  8192
102
103
2.88k
  Image
104
2.88k
    *image;
105
106
2.88k
  int
107
2.88k
    status;
108
109
2.88k
  size_t
110
2.88k
    length;
111
112
2.88k
  unsigned long
113
2.88k
    y;
114
115
2.88k
  register PixelPacket
116
2.88k
    *q;
117
118
2.88k
  register unsigned char
119
2.88k
    *p;
120
121
2.88k
  size_t
122
2.88k
    count;
123
124
2.88k
  struct jbg_dec_state
125
2.88k
    jbig_info;
126
127
2.88k
  unsigned char
128
2.88k
    *buffer;
129
130
  /*
131
    Open image file.
132
  */
133
2.88k
  assert(image_info != (const ImageInfo *) NULL);
134
2.88k
  assert(image_info->signature == MagickSignature);
135
2.88k
  assert(exception != (ExceptionInfo *) NULL);
136
2.88k
  assert(exception->signature == MagickSignature);
137
2.88k
  image=AllocateImage(image_info);
138
2.88k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
139
2.88k
  if (status == False)
140
2.88k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
141
  /*
142
    Initialize JBIG toolkit.
143
  */
144
2.88k
  jbg_dec_init(&jbig_info);
145
146
  /*
147
    Attempt to set maximum image dimensions based on resource limits.
148
149
    This does work in normal cases, but in other cases the first call
150
    to jbg_dec_in() takes a very long time, and it returns large image
151
    dimensions anyway.
152
  */
153
2.88k
  {
154
2.88k
    magick_int64_t
155
2.88k
      width_limit,
156
2.88k
      height_limit,
157
2.88k
      pixels_limit;
158
159
2.88k
    width_limit = GetMagickResourceLimit(WidthResource);
160
2.88k
    height_limit = GetMagickResourceLimit(HeightResource);
161
2.88k
    pixels_limit = GetMagickResourceLimit(PixelsResource);
162
163
2.88k
    if (MagickResourceInfinity != width_limit)
164
2.88k
      if ((image->columns == 0) || (image->columns > (unsigned long) width_limit))
165
2.88k
        image->columns = (unsigned long) width_limit;
166
167
2.88k
    if (MagickResourceInfinity != height_limit)
168
2.88k
      if ((image->rows == 0) || (image->rows > (unsigned long) height_limit))
169
2.88k
        image->rows = (unsigned long) height_limit;
170
171
2.88k
    if ((MagickResourceInfinity != pixels_limit) &&
172
2.88k
        ((magick_int64_t) (image->columns*image->rows)) > pixels_limit)
173
0
      {
174
0
        magick_int64_t max_dimension = sqrt((double) pixels_limit);
175
0
        image->columns = (unsigned long) max_dimension;
176
0
        image->rows = (unsigned long) max_dimension;
177
0
      }
178
179
2.88k
    if (image->logging)
180
2.88k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
181
2.88k
                            "JBIG: Setting maximum dimensions %lux%lu",
182
2.88k
                            image->columns, image->rows);
183
2.88k
    jbg_dec_maxsize(&jbig_info,(unsigned long) image->columns,
184
2.88k
                    (unsigned long) image->rows);
185
2.88k
  }
186
187
2.88k
  image->depth=1;
188
  /*
189
    Read JBIG file.
190
  */
191
2.88k
  buffer=MagickAllocateMemory(unsigned char *,MaxBufferSize);
192
2.88k
  if (buffer == (unsigned char *) NULL)
193
2.88k
    ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
194
2.88k
  status=JBG_EAGAIN;
195
  /* FIXME: Should handle JBG_EOK_INTR for multi-resolution support */
196
2.88k
  if (image->logging)
197
2.88k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
198
2.88k
                          "JBIG: Entering jbg_dec_in() decode loop...");
199
2.88k
  do
200
9.04k
    {
201
9.04k
      length=(long) ReadBlob(image,MaxBufferSize,(char *) buffer);
202
9.04k
      if (length == 0)
203
1.23k
        break;
204
7.81k
      p=buffer;
205
7.81k
      count=0;
206
15.6k
      while ((length > 0) && (status == JBG_EAGAIN))
207
7.81k
        {
208
7.81k
          status=jbg_dec_in(&jbig_info,p,length,&count);
209
7.81k
          if (image->logging)
210
7.81k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
211
7.81k
                                  "JBIG: jbg_dec_in() returns 0x%02x (\"%s\")",
212
7.81k
                                  status, jbg_strerror(status));
213
7.81k
          p+=count;
214
7.81k
          length-=count;
215
7.81k
        }
216
7.81k
    } while (status == JBG_EAGAIN);
217
2.88k
  if (JBG_EOK != status)
218
2.00k
    {
219
2.00k
      jbg_dec_free(&jbig_info);
220
2.00k
      MagickFreeMemory(buffer);
221
2.00k
      ThrowReaderException(CorruptImageError,CorruptImage,image);
222
0
    }
223
  /*
224
    Create colormap.
225
  */
226
885
  image->columns=jbg_dec_getwidth(&jbig_info);
227
885
  image->rows=jbg_dec_getheight(&jbig_info);
228
885
  if ((image_info->type != GrayscaleType) &&
229
885
      (image_info->type != TrueColorType))
230
885
    {
231
885
      if (image->logging)
232
885
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
233
885
                              "Allocating colormap...");
234
885
      if (!AllocateImageColormap(image,2))
235
0
        {
236
0
          MagickFreeMemory(buffer);
237
238
0
        }
239
885
      image->colormap[0].red=0;
240
885
      image->colormap[0].green=0;
241
885
      image->colormap[0].blue=0;
242
885
      image->colormap[0].opacity=0;
243
885
      image->colormap[1].red=MaxRGB;
244
885
      image->colormap[1].green=MaxRGB;
245
885
      image->colormap[1].blue=MaxRGB;
246
885
      image->colormap[1].opacity=0;
247
885
    }
248
885
  image->x_resolution=300;
249
885
  image->y_resolution=300;
250
885
  image->is_grayscale=MagickTrue;
251
885
  image->is_monochrome=MagickTrue;
252
885
  image->colorspace=GRAYColorspace;
253
885
  if (image->logging)
254
885
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
255
885
                          "JBIG: %lux%lu, resolution %gx%g",
256
885
                          image->columns, image->rows,
257
885
                          image->x_resolution,image->y_resolution);
258
885
  if (image_info->ping)
259
0
    {
260
0
      jbg_dec_free(&jbig_info);
261
0
      MagickFreeMemory(buffer);
262
0
      CloseBlob(image);
263
0
      return(image);
264
0
    }
265
885
  if (CheckImagePixelLimits(image, exception) != MagickPass)
266
110
    {
267
110
      jbg_dec_free(&jbig_info);
268
110
      MagickFreeMemory(buffer);
269
110
      ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
270
0
    }
271
  /*
272
    Convert bitmap image to pixel packets.
273
  */
274
775
  {
275
775
    ImportPixelAreaOptions
276
775
      import_options;
277
278
775
    ImportPixelAreaInfo
279
775
      import_info;
280
281
775
    ImportPixelAreaOptionsInit(&import_options);
282
775
    import_options.grayscale_miniswhite=MagickTrue;
283
775
    p=jbg_dec_getimage(&jbig_info,0);
284
857k
    for (y=0; y < image->rows; y++)
285
856k
      {
286
856k
        q=SetImagePixels(image,0,y,image->columns,1);
287
856k
        if (q == (PixelPacket *) NULL)
288
0
          break;
289
856k
        if (!ImportImagePixelArea(image,GrayQuantum,1,p,&import_options,&import_info))
290
0
          break;
291
856k
        p+=import_info.bytes_imported;
292
856k
        if (!SyncImagePixels(image))
293
0
          break;
294
856k
        if (QuantumTick(y,image->rows))
295
73.2k
          if (!MagickMonitorFormatted(y,image->rows,exception,LoadImageText,
296
73.2k
                                      image->filename,
297
73.2k
                                      image->columns,image->rows))
298
0
            break;
299
856k
      }
300
775
  }
301
  /*
302
    Free scale resource.
303
  */
304
775
  jbg_dec_free(&jbig_info);
305
775
  MagickFreeMemory(buffer);
306
775
  CloseBlob(image);
307
775
  image->is_grayscale=MagickTrue;
308
775
  image->is_monochrome=MagickTrue;
309
775
  StopTimer(&image->timer);
310
775
  return(image);
311
885
}
312
#endif
313

314
/*
315
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316
%                                                                             %
317
%                                                                             %
318
%                                                                             %
319
%   R e g i s t e r J B I G I m a g e                                         %
320
%                                                                             %
321
%                                                                             %
322
%                                                                             %
323
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324
%
325
%  Method RegisterJBIGImage adds attributes for the JBIG image format to
326
%  the list of supported formats.  The attributes include the image format
327
%  tag, a method to read and/or write the format, whether the format
328
%  supports the saving of more than one frame to the same file or blob,
329
%  whether the format supports native in-memory I/O, and a brief
330
%  description of the format.
331
%
332
%  The format of the RegisterJBIGImage method is:
333
%
334
%      RegisterJBIGImage(void)
335
%
336
*/
337
ModuleExport void RegisterJBIGImage(void)
338
2
{
339
2
  static const char
340
2
    description[]="Joint Bi-level Image experts Group interchange format";
341
342
2
  static const char
343
2
    version[]="JBIG-Kit " JBG_VERSION " (" JBG_LICENCE " license)";
344
345
2
  MagickInfo
346
2
    *entry;
347
348
2
  entry=SetMagickInfo("BIE");
349
2
#if defined(HasJBIG)
350
2
  entry->decoder=(DecoderHandler) ReadJBIGImage;
351
2
  entry->encoder=(EncoderHandler) WriteJBIGImage;
352
2
#endif
353
2
  entry->adjoin=False;
354
2
  entry->description=description;
355
2
    entry->version=version;
356
2
  entry->module="JBIG";
357
2
  entry->coder_class=StableCoderClass;
358
2
  (void) RegisterMagickInfo(entry);
359
360
2
  entry=SetMagickInfo("JBG");
361
2
#if defined(HasJBIG)
362
2
  entry->decoder=(DecoderHandler) ReadJBIGImage;
363
2
  entry->encoder=(EncoderHandler) WriteJBIGImage;
364
2
#endif
365
2
  entry->description=description;
366
2
  entry->version=version;
367
2
  entry->module="JBIG";
368
2
  entry->coder_class=StableCoderClass;
369
2
  (void) RegisterMagickInfo(entry);
370
371
2
  entry=SetMagickInfo("JBIG");
372
2
#if defined(HasJBIG)
373
2
  entry->decoder=(DecoderHandler) ReadJBIGImage;
374
2
  entry->encoder=(EncoderHandler) WriteJBIGImage;
375
2
#endif
376
2
  entry->description=description;
377
2
  entry->version=version;
378
2
  entry->module="JBIG";
379
2
  entry->coder_class=UnstableCoderClass;
380
2
  (void) RegisterMagickInfo(entry);
381
2
}
382

383
/*
384
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385
%                                                                             %
386
%                                                                             %
387
%                                                                             %
388
%   U n r e g i s t e r J B I G I m a g e                                     %
389
%                                                                             %
390
%                                                                             %
391
%                                                                             %
392
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393
%
394
%  Method UnregisterJBIGImage removes format registrations made by the
395
%  JBIG module from the list of supported formats.
396
%
397
%  The format of the UnregisterJBIGImage method is:
398
%
399
%      UnregisterJBIGImage(void)
400
%
401
*/
402
ModuleExport void UnregisterJBIGImage(void)
403
0
{
404
0
  (void) UnregisterMagickInfo("BIE");
405
0
  (void) UnregisterMagickInfo("JBG");
406
0
  (void) UnregisterMagickInfo("JBIG");
407
0
}
408

409
#if defined(HasJBIG)
410
/*
411
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412
%                                                                             %
413
%                                                                             %
414
%                                                                             %
415
%   W r i t e J B I G I m a g e                                               %
416
%                                                                             %
417
%                                                                             %
418
%                                                                             %
419
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420
%
421
%  Method WriteJBIGImage writes an image in the JBIG encoded image format.
422
%
423
%  The format of the WriteJBIGImage method is:
424
%
425
%      unsigned int WriteJBIGImage(const ImageInfo *image_info,Image *image)
426
%
427
%  A description of each parameter follows.
428
%
429
%    o status: Method WriteJBIGImage return True if the image is written.
430
%      False is returned is there is a memory shortage or if the image file
431
%      fails to write.
432
%
433
%    o image_info: Specifies a pointer to a ImageInfo structure.
434
%
435
%    o image:  A pointer to an Image structure.
436
%
437
%
438
*/
439
440
static void JBIGEncode(unsigned char *pixels,size_t length,void *data)
441
76.8k
{
442
76.8k
  Image
443
76.8k
    *image;
444
445
76.8k
  image=(Image *) data;
446
76.8k
  (void) WriteBlob(image,length,pixels);
447
76.8k
}
448
449
static unsigned int WriteJBIGImage(const ImageInfo *image_info,Image *image)
450
775
{
451
775
  double
452
775
    jbig_lib_version;
453
454
775
  unsigned long
455
775
    y;
456
457
775
  register const PixelPacket
458
775
    *p;
459
460
775
  register unsigned char
461
775
    *q;
462
463
775
  struct jbg_enc_state
464
775
    jbig_info;
465
466
775
  unsigned char
467
775
    *pixels;
468
469
775
  size_t
470
775
    bytes_per_row;
471
472
775
  unsigned int
473
775
    status;
474
475
775
  unsigned long
476
775
    scene;
477
478
775
  size_t
479
775
    image_list_length;
480
481
  /*
482
    Open image file.
483
  */
484
775
  assert(image_info != (const ImageInfo *) NULL);
485
775
  assert(image_info->signature == MagickSignature);
486
775
  assert(image != (Image *) NULL);
487
775
  assert(image->signature == MagickSignature);
488
775
  image_list_length=GetImageListLength(image);
489
775
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
490
775
  if (status == False)
491
775
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
492
775
  jbig_lib_version=strtod(JBG_VERSION, (char **)NULL);
493
775
  scene=0;
494
775
  do
495
775
  {
496
    /*
497
      Ensure that image is in an RGB space.
498
    */
499
775
    if (TransformColorspace(image,RGBColorspace) == MagickFail)
500
775
      ThrowWriterException(CoderError,UnableToTransformColorspace,image);
501
    /*
502
      Allocate pixel data.
503
    */
504
775
    bytes_per_row=((image->columns+7) >> 3);
505
775
    pixels=MagickAllocateResourceLimitedArray(unsigned char *,bytes_per_row,image->rows);
506
775
    if (pixels == (unsigned char *) NULL)
507
775
      ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
508
    /*
509
      Convert pixels to a bitmap.
510
    */
511
775
    {
512
775
      ExportPixelAreaOptions
513
775
        export_options;
514
515
775
      ExportPixelAreaInfo
516
775
        export_info;
517
518
775
      ExportPixelAreaOptionsInit(&export_options);
519
775
      export_options.grayscale_miniswhite=MagickTrue;
520
775
      q=pixels;
521
857k
      for (y=0; y < image->rows; y++)
522
856k
        {
523
856k
          p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
524
856k
          if (p == (const PixelPacket *) NULL)
525
0
            break;
526
856k
          if (ExportImagePixelArea(image,GrayQuantum,1,q,
527
856k
                                   &export_options,&export_info) == MagickFail)
528
0
            break;
529
856k
          q+=export_info.bytes_exported;
530
856k
          if (image->previous == (Image *) NULL)
531
856k
            if (QuantumTick(y,image->rows))
532
73.2k
              if (!MagickMonitorFormatted(y,image->rows,&image->exception,
533
73.2k
                                          SaveImageText,image->filename,
534
73.2k
                                          image->columns,image->rows))
535
0
                break;
536
856k
        }
537
775
    }
538
    /*
539
      Initialize JBIG info structure.
540
    */
541
775
    jbg_enc_init(&jbig_info,image->columns,image->rows,1,&pixels,
542
775
      (void (*)(unsigned char *,size_t,void *)) JBIGEncode,image);
543
775
    if (image_info->subimage != 0)
544
0
      {
545
0
        jbg_enc_layers(&jbig_info,(int) image_info->subimage);
546
0
      }
547
775
    else
548
775
      {
549
775
        long
550
775
          sans_offset;
551
552
775
        unsigned long
553
775
          x_resolution,
554
775
          y_resolution;
555
556
775
        x_resolution=640;
557
775
        y_resolution=480;
558
775
        sans_offset=0;
559
775
        if (image_info->density != (char *) NULL)
560
0
          (void) GetGeometry(image_info->density,&sans_offset,&sans_offset,
561
0
            &x_resolution,&y_resolution);
562
775
        (void) jbg_enc_lrlmax(&jbig_info,x_resolution,y_resolution);
563
775
      }
564
775
    (void) jbg_enc_lrange(&jbig_info,-1,-1);
565
775
    jbg_enc_options(&jbig_info, /* Encoder state */
566
775
                    JBG_ILEAVE| JBG_SMID, /* Order */
567
775
                    JBG_TPDON | JBG_TPBON | JBG_DPON, /* Options */
568
                    /* Lines per stripe in resolution layer 0. (was -1)*/
569
775
                    (jbig_lib_version < 1.6 ? -1 : 0),
570
775
                    -1, /* mx */
571
775
                    -1); /* my */
572
    /*
573
      Write JBIG image.
574
    */
575
775
    jbg_enc_out(&jbig_info);
576
775
    jbg_enc_free(&jbig_info);
577
775
    MagickFreeResourceLimitedMemory(unsigned char *,pixels);
578
775
    if (image->next == (Image *) NULL)
579
775
      break;
580
0
    image=SyncNextImageInList(image);
581
0
    if (!MagickMonitorFormatted(scene++,image_list_length,
582
0
                                &image->exception,SaveImagesText,
583
0
                                image->filename))
584
0
      break;
585
0
  } while (image_info->adjoin);
586
775
  if (image_info->adjoin)
587
775
    while (image->previous != (Image *) NULL)
588
0
      image=image->previous;
589
775
  status &= CloseBlob(image);
590
775
  return(status);
591
775
}
592
#endif