Coverage Report

Created: 2025-12-03 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/load-jpeg.c
Line
Count
Source
1
// Copyright (C) 2004-2023 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
25
#include <math.h>
26
#include <stdio.h>
27
#include <string.h>
28
#include <limits.h>
29
30
#include <jpeglib.h>
31
32
#ifdef SHARE_JPEG
33
34
#define JZ_CTX_FROM_CINFO(c) (fz_context *)((c)->client_data)
35
36
static void fz_jpg_mem_init(j_common_ptr cinfo, fz_context *ctx)
37
{
38
  cinfo->client_data = ctx;
39
}
40
41
#define fz_jpg_mem_term(cinfo)
42
43
#else /* SHARE_JPEG */
44
45
typedef void * backing_store_ptr;
46
#include "jmemcust.h"
47
48
24
#define JZ_CTX_FROM_CINFO(c) (fz_context *)(GET_CUST_MEM_DATA(c)->priv)
49
50
static void *
51
fz_jpg_mem_alloc(j_common_ptr cinfo, size_t size)
52
11
{
53
11
  fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
54
11
  return fz_malloc_no_throw(ctx, size);
55
11
}
56
57
static void
58
fz_jpg_mem_free(j_common_ptr cinfo, void *object, size_t size)
59
11
{
60
11
  fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
61
11
  fz_free(ctx, object);
62
11
}
63
64
static void
65
fz_jpg_mem_init(j_common_ptr cinfo, fz_context *ctx)
66
2
{
67
2
  jpeg_cust_mem_data *custmptr;
68
2
  custmptr = fz_malloc_struct(ctx, jpeg_cust_mem_data);
69
2
  if (!jpeg_cust_mem_init(custmptr, (void *) ctx, NULL, NULL, NULL,
70
2
        fz_jpg_mem_alloc, fz_jpg_mem_free,
71
2
        fz_jpg_mem_alloc, fz_jpg_mem_free, NULL))
72
0
  {
73
0
    fz_free(ctx, custmptr);
74
0
    fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize custom JPEG memory handler");
75
0
  }
76
2
  cinfo->client_data = custmptr;
77
2
}
78
79
static void
80
fz_jpg_mem_term(j_common_ptr cinfo)
81
2
{
82
2
  if (cinfo->client_data)
83
2
  {
84
2
    fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
85
2
    fz_free(ctx, cinfo->client_data);
86
2
    cinfo->client_data = NULL;
87
2
  }
88
2
}
89
90
#endif /* SHARE_JPEG */
91
92
static void output_message(j_common_ptr cinfo)
93
0
{
94
  /* swallow message */
95
0
}
96
97
static void error_exit(j_common_ptr cinfo)
98
0
{
99
0
  char msg[JMSG_LENGTH_MAX];
100
0
  fz_context *ctx = JZ_CTX_FROM_CINFO(cinfo);
101
102
0
  cinfo->err->format_message(cinfo, msg);
103
0
  fz_throw(ctx, FZ_ERROR_LIBRARY, "jpeg error: %s", msg);
104
0
}
105
106
static void init_source(j_decompress_ptr cinfo)
107
2
{
108
  /* nothing to do */
109
2
}
110
111
static void term_source(j_decompress_ptr cinfo)
112
0
{
113
  /* nothing to do */
114
0
}
115
116
static boolean fill_input_buffer(j_decompress_ptr cinfo)
117
0
{
118
0
  static unsigned char eoi[2] = { 0xFF, JPEG_EOI };
119
0
  struct jpeg_source_mgr *src = cinfo->src;
120
0
  src->next_input_byte = eoi;
121
0
  src->bytes_in_buffer = 2;
122
0
  return 1;
123
0
}
124
125
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
126
0
{
127
0
  struct jpeg_source_mgr *src = cinfo->src;
128
0
  if (num_bytes > 0)
129
0
  {
130
0
    size_t skip = (size_t)num_bytes; /* size_t may be 64bit */
131
0
    if (skip > src->bytes_in_buffer)
132
0
      skip = (size_t)src->bytes_in_buffer;
133
0
    src->next_input_byte += skip;
134
0
    src->bytes_in_buffer -= skip;
135
0
  }
136
0
}
137
138
static inline int read_value(const unsigned char *data, int bytes, int is_big_endian)
139
89
{
140
89
  int value = 0;
141
89
  if (!is_big_endian)
142
55
    data += bytes;
143
361
  for (; bytes > 0; bytes--)
144
272
    value = (value << 8) | (is_big_endian ? *data++ : *--data);
145
89
  return value;
146
89
}
147
148
enum {
149
  MAX_ICC_PARTS = 256
150
};
151
152
static fz_colorspace *extract_icc_profile(fz_context *ctx, jpeg_saved_marker_ptr init_marker, int output_components, fz_colorspace *colorspace)
153
2
{
154
2
#if FZ_ENABLE_ICC
155
2
  const char idseq[] = { 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0'};
156
2
  jpeg_saved_marker_ptr marker = init_marker;
157
2
  fz_buffer *buf = NULL;
158
2
  fz_colorspace *icc;
159
2
  int part = 1;
160
2
  int parts = MAX_ICC_PARTS;
161
2
  const unsigned char *data;
162
2
  size_t size;
163
164
2
  fz_var(buf);
165
166
2
  if (init_marker == NULL)
167
0
    return colorspace;
168
169
4
  fz_try(ctx)
170
4
  {
171
4
    while (part < parts && marker != NULL)
172
2
    {
173
5
      for (marker = init_marker; marker != NULL; marker = marker->next)
174
4
      {
175
4
        if (marker->marker != JPEG_APP0 + 2)
176
3
          continue;
177
1
        if (marker->data_length < nelem(idseq) + 2)
178
0
          continue;
179
1
        if (memcmp(marker->data, idseq, nelem(idseq)))
180
0
          continue;
181
1
        if (marker->data[nelem(idseq)] != part)
182
0
          continue;
183
184
1
        if (parts == MAX_ICC_PARTS)
185
1
          parts = marker->data[nelem(idseq) + 1];
186
0
        else if (marker->data[nelem(idseq) + 1] != parts)
187
0
          fz_warn(ctx, "inconsistent number of icc profile chunks in jpeg");
188
1
        if (part > parts)
189
0
        {
190
0
          fz_warn(ctx, "skipping out of range icc profile chunk in jpeg");
191
0
          continue;
192
0
        }
193
194
1
        data = marker->data + 14;
195
1
        size = marker->data_length - 14;
196
197
1
        if (!buf)
198
1
          buf = fz_new_buffer_from_copied_data(ctx, data, size);
199
0
        else
200
0
          fz_append_data(ctx, buf, data, size);
201
202
1
        part++;
203
1
        break;
204
1
      }
205
2
    }
206
207
2
    if (buf)
208
1
    {
209
1
      icc = fz_new_icc_colorspace(ctx, fz_colorspace_type(ctx, colorspace), 0, NULL, buf);
210
1
      fz_drop_colorspace(ctx, colorspace);
211
1
      colorspace = icc;
212
1
    }
213
2
  }
214
4
  fz_always(ctx)
215
2
    fz_drop_buffer(ctx, buf);
216
2
  fz_catch(ctx)
217
0
  {
218
0
    fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
219
0
    fz_report_error(ctx);
220
0
    fz_warn(ctx, "ignoring embedded ICC profile in JPEG");
221
0
  }
222
223
2
  return colorspace;
224
#else
225
  return colorspace;
226
#endif
227
2
}
228
229
/* Returns true if <x> can be represented as an integer without overflow.
230
 *
231
 * We can't use comparisons such as 'return x < INT_MAX' because INT_MAX is
232
 * not safely convertible to float - it ends up as INT_MAX+1 so the comparison
233
 * doesn't do what we want.
234
 *
235
 * Instead we do a round-trip conversion and return true if this differs by
236
 * less than 1. This relies on high adjacent float values that differ by more
237
 * than 1, actually being exact integers, so the round-trip doesn't change the
238
 * value.
239
 */
240
static int float_can_be_int(float x)
241
2
{
242
2
  return fabsf(x - (float)(int) x) < 1;
243
2
}
244
245
static uint8_t exif_orientation_to_mupdf[9] = { 0, 1, 5, 3, 7, 6, 4, 8, 2 };
246
247
static int extract_exif_resolution(jpeg_saved_marker_ptr marker,
248
  int *xres, int *yres, uint8_t *orientation)
249
2
{
250
2
  int is_big_endian, orient;
251
2
  const unsigned char *data;
252
2
  unsigned int offset, ifd_len, res_type = 0;
253
2
  float x_res = 0, y_res = 0;
254
255
2
  if (!marker || marker->marker != JPEG_APP0 + 1 || marker->data_length < 14)
256
0
    return 0;
257
2
  data = (const unsigned char *)marker->data;
258
2
  if (read_value(data, 4, 1) != 0x45786966 /* Exif */ || read_value(data + 4, 2, 1) != 0x0000)
259
0
    return 0;
260
2
  if (read_value(data + 6, 4, 1) == 0x49492A00)
261
1
    is_big_endian = 0;
262
1
  else if (read_value(data + 6, 4, 1) == 0x4D4D002A)
263
1
    is_big_endian = 1;
264
0
  else
265
0
    return 0;
266
267
2
  offset = read_value(data + 10, 4, is_big_endian) + 6;
268
2
  if (offset < 14 || offset > marker->data_length - 2)
269
0
    return 0;
270
2
  ifd_len = read_value(data + offset, 2, is_big_endian);
271
20
  for (offset += 2; ifd_len > 0 && offset + 12 < marker->data_length; ifd_len--, offset += 12)
272
18
  {
273
18
    int tag = read_value(data + offset, 2, is_big_endian);
274
18
    int type = read_value(data + offset + 2, 2, is_big_endian);
275
18
    int count = read_value(data + offset + 4, 4, is_big_endian);
276
18
    unsigned int value_off = read_value(data + offset + 8, 4, is_big_endian) + 6;
277
18
    switch (tag)
278
18
    {
279
1
    case 0x112:
280
1
      if (type == 3 && count == 1) {
281
1
        orient = read_value(data + offset + 8, 2, is_big_endian);
282
1
        if (orient >= 1 && orient <= 8 && orientation)
283
1
          *orientation = exif_orientation_to_mupdf[orient];
284
1
      }
285
1
      break;
286
1
    case 0x11A:
287
1
      if (type == 5 && value_off > offset && value_off <= marker->data_length - 8)
288
1
        x_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
289
1
      break;
290
1
    case 0x11B:
291
1
      if (type == 5 && value_off > offset && value_off <= marker->data_length - 8)
292
1
        y_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
293
1
      break;
294
1
    case 0x128:
295
1
      if (type == 3 && count == 1)
296
1
        res_type = read_value(data + offset + 8, 2, is_big_endian);
297
1
      break;
298
18
    }
299
18
  }
300
301
2
  if (x_res <= 0 || !float_can_be_int(x_res) || y_res <= 0 || !float_can_be_int(y_res))
302
1
    return 0;
303
1
  if (res_type == 2)
304
1
  {
305
1
    *xres = (int)x_res;
306
1
    *yres = (int)y_res;
307
1
  }
308
0
  else if (res_type == 3)
309
0
  {
310
0
    *xres = (int)(x_res * 254 / 100);
311
0
    *yres = (int)(y_res * 254 / 100);
312
0
  }
313
0
  else
314
0
  {
315
0
    *xres = 0;
316
0
    *yres = 0;
317
0
  }
318
1
  return 1;
319
2
}
320
321
static int extract_app13_resolution(jpeg_saved_marker_ptr marker, int *xres, int *yres)
322
1
{
323
1
  const unsigned char *data, *data_end;
324
325
1
  if (!marker || marker->marker != JPEG_APP0 + 13 || marker->data_length < 42 ||
326
0
    strcmp((const char *)marker->data, "Photoshop 3.0") != 0)
327
1
  {
328
1
    return 0;
329
1
  }
330
331
0
  data = (const unsigned char *)marker->data;
332
0
  data_end = data + marker->data_length;
333
0
  for (data += 14; data + 12 < data_end; ) {
334
0
    int data_size = -1;
335
0
    int tag = read_value(data + 4, 2, 1);
336
0
    int value_off = 11 + read_value(data + 6, 2, 1);
337
0
    if (value_off % 2 == 1)
338
0
      value_off++;
339
0
    if (read_value(data, 4, 1) == 0x3842494D /* 8BIM */ && value_off <= data_end - data)
340
0
      data_size = read_value(data + value_off - 4, 4, 1);
341
0
    if (data_size < 0 || data_size > data_end - data - value_off)
342
0
      return 0;
343
0
    if (tag == 0x3ED && data_size == 16)
344
0
    {
345
0
      *xres = read_value(data + value_off, 2, 1);
346
0
      *yres = read_value(data + value_off + 8, 2, 1);
347
0
      return 1;
348
0
    }
349
0
    if (data_size % 2 == 1)
350
0
      data_size++;
351
0
    data += value_off + data_size;
352
0
  }
353
354
0
  return 0;
355
0
}
356
357
static void invert_cmyk(unsigned char *p, int n)
358
0
{
359
0
  int i;
360
0
  for (i = 0; i < n; ++i)
361
0
    p[i] = 255 - p[i];
362
0
}
363
364
fz_pixmap *
365
fz_load_jpeg(fz_context *ctx, const unsigned char *rbuf, size_t rlen)
366
0
{
367
0
  struct jpeg_decompress_struct cinfo;
368
0
  struct jpeg_error_mgr err;
369
0
  struct jpeg_source_mgr src;
370
0
  unsigned char *row[1], *sp, *dp;
371
0
  fz_colorspace *colorspace = NULL;
372
0
  unsigned int x;
373
0
  int k;
374
0
  size_t stride;
375
0
  fz_pixmap *image = NULL;
376
377
0
  fz_var(colorspace);
378
0
  fz_var(image);
379
0
  fz_var(row);
380
381
0
  row[0] = NULL;
382
383
0
  cinfo.mem = NULL;
384
0
  cinfo.global_state = 0;
385
0
  cinfo.err = jpeg_std_error(&err);
386
0
  err.output_message = output_message;
387
0
  err.error_exit = error_exit;
388
389
0
  cinfo.client_data = NULL;
390
0
  fz_jpg_mem_init((j_common_ptr)&cinfo, ctx);
391
392
0
  fz_try(ctx)
393
0
  {
394
0
    jpeg_create_decompress(&cinfo);
395
396
0
    cinfo.src = &src;
397
0
    src.init_source = init_source;
398
0
    src.fill_input_buffer = fill_input_buffer;
399
0
    src.skip_input_data = skip_input_data;
400
0
    src.resync_to_restart = jpeg_resync_to_restart;
401
0
    src.term_source = term_source;
402
0
    src.next_input_byte = rbuf;
403
0
    src.bytes_in_buffer = rlen;
404
405
0
    jpeg_save_markers(&cinfo, JPEG_APP0+1, 0xffff);
406
0
    jpeg_save_markers(&cinfo, JPEG_APP0+13, 0xffff);
407
0
    jpeg_save_markers(&cinfo, JPEG_APP0+2, 0xffff);
408
409
0
    jpeg_read_header(&cinfo, 1);
410
411
0
    jpeg_start_decompress(&cinfo);
412
413
0
    if (cinfo.output_components == 1)
414
0
      colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
415
0
    else if (cinfo.output_components == 3)
416
0
      colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
417
0
    else if (cinfo.output_components == 4)
418
0
      colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
419
0
    colorspace = extract_icc_profile(ctx, cinfo.marker_list, cinfo.output_components, colorspace);
420
0
    if (!colorspace)
421
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "cannot determine colorspace");
422
423
0
    image = fz_new_pixmap(ctx, colorspace, cinfo.output_width, cinfo.output_height, NULL, 0);
424
425
0
    if (extract_exif_resolution(cinfo.marker_list, &image->xres, &image->yres, NULL))
426
0
      /* XPS prefers EXIF resolution to JFIF density */;
427
0
    else if (extract_app13_resolution(cinfo.marker_list, &image->xres, &image->yres))
428
0
      /* XPS prefers APP13 resolution to JFIF density */;
429
0
    else if (cinfo.density_unit == 1)
430
0
    {
431
0
      image->xres = cinfo.X_density;
432
0
      image->yres = cinfo.Y_density;
433
0
    }
434
0
    else if (cinfo.density_unit == 2)
435
0
    {
436
0
      image->xres = cinfo.X_density * 254 / 100;
437
0
      image->yres = cinfo.Y_density * 254 / 100;
438
0
    }
439
440
0
    if (image->xres <= 0) image->xres = 96;
441
0
    if (image->yres <= 0) image->yres = 96;
442
443
0
    fz_clear_pixmap(ctx, image);
444
445
0
    row[0] = fz_malloc(ctx, (size_t)cinfo.output_components * cinfo.output_width);
446
0
    dp = image->samples;
447
0
    stride = image->stride - image->w * (size_t)image->n;
448
0
    while (cinfo.output_scanline < cinfo.output_height)
449
0
    {
450
0
      jpeg_read_scanlines(&cinfo, row, 1);
451
452
      // Invert CMYK polarity for some CMYK images (see comment in filter-dct for details).
453
0
      if (cinfo.out_color_space == JCS_CMYK && cinfo.Adobe_transform == 2)
454
0
        invert_cmyk(row[0], image->stride);
455
456
0
      sp = row[0];
457
0
      for (x = 0; x < cinfo.output_width; x++)
458
0
      {
459
0
        for (k = 0; k < cinfo.output_components; k++)
460
0
          *dp++ = *sp++;
461
0
      }
462
0
      dp += stride;
463
0
    }
464
0
  }
465
0
  fz_always(ctx)
466
0
  {
467
0
    fz_drop_colorspace(ctx, colorspace);
468
0
    fz_free(ctx, row[0]);
469
0
    row[0] = NULL;
470
471
    /* We call jpeg_abort rather than the more usual
472
     * jpeg_finish_decompress here. This has the same effect,
473
     * but doesn't spew warnings if we didn't read enough data etc.
474
     * Annoyingly jpeg_abort can throw
475
     */
476
0
    fz_try(ctx)
477
0
      jpeg_abort((j_common_ptr)&cinfo);
478
0
    fz_catch(ctx)
479
0
    {
480
      /* Ignore any errors here */
481
0
    }
482
483
0
    jpeg_destroy_decompress(&cinfo);
484
0
    fz_jpg_mem_term((j_common_ptr)&cinfo);
485
0
  }
486
0
  fz_catch(ctx)
487
0
  {
488
0
    fz_drop_pixmap(ctx, image);
489
0
    fz_rethrow(ctx);
490
0
  }
491
492
0
  return image;
493
0
}
494
495
void
496
fz_load_jpeg_info(fz_context *ctx, const unsigned char *rbuf, size_t rlen, int *xp, int *yp, int *xresp, int *yresp, fz_colorspace **cspacep, uint8_t *orientation)
497
2
{
498
2
  struct jpeg_decompress_struct cinfo;
499
2
  struct jpeg_error_mgr err;
500
2
  struct jpeg_source_mgr src;
501
2
  fz_colorspace *icc = NULL;
502
503
2
  *cspacep = NULL;
504
2
  if (orientation)
505
2
    *orientation = 0;
506
507
2
  cinfo.mem = NULL;
508
2
  cinfo.global_state = 0;
509
2
  cinfo.err = jpeg_std_error(&err);
510
2
  err.error_exit = error_exit;
511
512
2
  cinfo.client_data = NULL;
513
2
  fz_jpg_mem_init((j_common_ptr)&cinfo, ctx);
514
515
4
  fz_try(ctx)
516
4
  {
517
2
    jpeg_create_decompress(&cinfo);
518
519
2
    cinfo.src = &src;
520
2
    src.init_source = init_source;
521
2
    src.fill_input_buffer = fill_input_buffer;
522
2
    src.skip_input_data = skip_input_data;
523
2
    src.resync_to_restart = jpeg_resync_to_restart;
524
2
    src.term_source = term_source;
525
2
    src.next_input_byte = rbuf;
526
2
    src.bytes_in_buffer = rlen;
527
528
2
    jpeg_save_markers(&cinfo, JPEG_APP0+1, 0xffff);
529
2
    jpeg_save_markers(&cinfo, JPEG_APP0+13, 0xffff);
530
2
    jpeg_save_markers(&cinfo, JPEG_APP0+2, 0xffff);
531
532
2
    jpeg_read_header(&cinfo, 1);
533
534
2
    *xp = cinfo.image_width;
535
2
    *yp = cinfo.image_height;
536
537
2
    if (cinfo.num_components == 1)
538
0
      *cspacep = fz_keep_colorspace(ctx, fz_device_gray(ctx));
539
2
    else if (cinfo.num_components == 3)
540
2
      *cspacep = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
541
0
    else if (cinfo.num_components == 4)
542
0
      *cspacep = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
543
2
    *cspacep = extract_icc_profile(ctx, cinfo.marker_list, cinfo.num_components, *cspacep);
544
2
    if (!*cspacep)
545
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "cannot determine colorspace");
546
547
2
    if (extract_exif_resolution(cinfo.marker_list, xresp, yresp, orientation))
548
1
      /* XPS prefers EXIF resolution to JFIF density */;
549
1
    else if (extract_app13_resolution(cinfo.marker_list, xresp, yresp))
550
0
      /* XPS prefers APP13 resolution to JFIF density */;
551
1
    else if (cinfo.density_unit == 1)
552
0
    {
553
0
      *xresp = cinfo.X_density;
554
0
      *yresp = cinfo.Y_density;
555
0
    }
556
1
    else if (cinfo.density_unit == 2)
557
0
    {
558
0
      *xresp = cinfo.X_density * 254 / 100;
559
0
      *yresp = cinfo.Y_density * 254 / 100;
560
0
    }
561
1
    else
562
1
    {
563
1
      *xresp = 0;
564
1
      *yresp = 0;
565
1
    }
566
567
2
    if (*xresp <= 0) *xresp = 96;
568
2
    if (*yresp <= 0) *yresp = 96;
569
2
  }
570
4
  fz_always(ctx)
571
2
  {
572
2
    jpeg_destroy_decompress(&cinfo);
573
2
    fz_jpg_mem_term((j_common_ptr)&cinfo);
574
2
  }
575
2
  fz_catch(ctx)
576
0
  {
577
0
    fz_drop_colorspace(ctx, icc);
578
0
    fz_rethrow(ctx);
579
0
  }
580
2
}