Coverage Report

Created: 2025-09-04 06:50

/src/mupdf/source/fitz/load-pnm.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2025 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 "pixmap-imp.h"
26
27
#include <string.h>
28
#include <limits.h>
29
30
enum
31
{
32
  PAM_UNKNOWN = 0,
33
  PAM_BW,
34
  PAM_BWA,
35
  PAM_GRAY,
36
  PAM_GRAYA,
37
  PAM_RGB,
38
  PAM_RGBA,
39
  PAM_CMYK,
40
  PAM_CMYKA,
41
};
42
43
enum
44
{
45
  TOKEN_UNKNOWN = 0,
46
  TOKEN_WIDTH,
47
  TOKEN_HEIGHT,
48
  TOKEN_DEPTH,
49
  TOKEN_MAXVAL,
50
  TOKEN_TUPLTYPE,
51
  TOKEN_ENDHDR,
52
};
53
54
enum
55
{
56
  ENDIAN_UNKNOWN = 0,
57
  ENDIAN_LITTLE,
58
  ENDIAN_BIG,
59
};
60
61
struct info
62
{
63
  int subimages;
64
  fz_colorspace *cs;
65
  int width, height;
66
  int maxval, bitdepth;
67
  int depth, alpha;
68
  int tupletype;
69
  int endian;
70
  float scale;
71
};
72
73
static inline int iswhiteeol(int a)
74
0
{
75
0
  switch (a) {
76
0
  case ' ': case '\t': case '\r': case '\n':
77
0
    return 1;
78
0
  }
79
0
  return 0;
80
0
}
81
82
static inline int iswhite(int a)
83
0
{
84
0
  switch (a) {
85
0
  case ' ': case '\t':
86
0
    return 1;
87
0
  }
88
0
  return 0;
89
0
}
90
91
static inline int bitdepth_from_maxval(int maxval)
92
0
{
93
0
  int depth = 0;
94
0
  while (maxval)
95
0
  {
96
0
    maxval >>= 1;
97
0
    depth++;
98
0
  }
99
0
  return depth;
100
0
}
101
102
static const unsigned char *
103
pnm_read_signature(fz_context *ctx, const unsigned char *p, const unsigned char *e, char *signature)
104
0
{
105
0
  if (e - p < 2)
106
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse magic number in pnm image");
107
0
  if (p[0] != 'P' || ((p[1] < '1' || p[1] > '7') && p[1] != 'F' && p[1] != 'f'))
108
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected signature in pnm image");
109
110
0
  signature[0] = *p++;
111
0
  signature[1] = *p++;
112
0
  return p;
113
0
}
114
115
static const unsigned char *
116
pnm_read_until_eol(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR)
117
0
{
118
0
  if (e - p < 1)
119
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse line in pnm image");
120
121
0
  while (p < e && ((acceptCR && *p != '\r' && *p != '\n') || (!acceptCR && *p != '\n')))
122
0
    p++;
123
124
0
  return p;
125
0
}
126
127
static const unsigned char *
128
pnm_read_eol(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR)
129
0
{
130
0
  if (e - p < 1)
131
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse end of line in pnm image");
132
0
  if ((acceptCR && *p != '\r' && *p != '\n') || (!acceptCR && *p != '\n'))
133
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected end of line in pnm image");
134
135
  /* CR, CRLF or LF depending on acceptCR. */
136
0
  if (acceptCR && *p == '\r')
137
0
    p++;
138
0
  if (p < e && *p == '\n')
139
0
    p++;
140
141
0
  return p;
142
0
}
143
144
static const unsigned char *
145
pnm_read_whites(fz_context *ctx, const unsigned char *p, const unsigned char *e, int required)
146
0
{
147
0
  if (required && e - p < 1)
148
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse whitespaces in pnm image");
149
0
  if (required && !iswhite(*p))
150
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected whitespaces in pnm image");
151
152
0
  while (p < e && iswhite(*p))
153
0
    p++;
154
155
0
  return p;
156
0
}
157
158
static const unsigned char *
159
pnm_read_white_or_eol(fz_context *ctx, const unsigned char *p, const unsigned char *e)
160
0
{
161
0
  if (e - p < 1)
162
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse whitespace/eol in pnm image");
163
0
  if (!iswhiteeol(*p))
164
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected whitespace/eol in pnm image");
165
166
0
  return ++p;
167
0
}
168
169
static const unsigned char *
170
pnm_read_whites_and_eols(fz_context *ctx, const unsigned char *p, const unsigned char *e, int required)
171
0
{
172
0
  if (required && e - p < 1)
173
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse whitespaces/eols in pnm image");
174
0
  if (required && !iswhiteeol(*p))
175
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected whitespaces/eols in pnm image");
176
177
0
  while (p < e && iswhiteeol(*p))
178
0
    p++;
179
180
0
  return p;
181
0
}
182
183
static const unsigned char *
184
pnm_read_comment(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR)
185
0
{
186
0
  if (e - p < 1)
187
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse line in pnm image");
188
189
0
  if (*p != '#')
190
0
    return p;
191
192
0
  return pnm_read_until_eol(ctx, p, e, acceptCR);
193
0
}
194
195
static const unsigned char *
196
pnm_read_comments(fz_context *ctx, const unsigned char *p, const unsigned char *e, int acceptCR)
197
0
{
198
0
  if (e - p < 1)
199
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse comment in pnm image");
200
201
0
  while (p < e && *p == '#')
202
0
  {
203
0
    p = pnm_read_comment(ctx, p, e, acceptCR);
204
0
    p = pnm_read_eol(ctx, p, e, acceptCR);
205
0
  }
206
207
0
  return p;
208
0
}
209
210
static const unsigned char *
211
pnm_read_digit(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *number)
212
0
{
213
0
  if (e - p < 1)
214
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse digit in pnm image");
215
0
  if (*p < '0' || *p > '1')
216
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected digit in pnm image");
217
218
0
  if (number)
219
0
    *number = *p - '0';
220
0
  p++;
221
222
0
  return p;
223
0
}
224
225
static const unsigned char *
226
pnm_read_int(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *number)
227
0
{
228
0
  if (e - p < 1)
229
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse integer in pnm image");
230
0
  if (*p < '0' || *p > '9')
231
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected integer in pnm image");
232
233
0
  while (p < e && *p >= '0' && *p <= '9')
234
0
  {
235
0
    if (number)
236
0
      *number = *number * 10 + *p - '0';
237
0
    p++;
238
0
  }
239
240
0
  return p;
241
0
}
242
243
static const unsigned char *
244
pnm_read_real(fz_context *ctx, const unsigned char *p, const unsigned char *e, float *number)
245
0
{
246
0
  const unsigned char *orig = p;
247
0
  char *buf, *end;
248
0
  size_t len;
249
250
0
  if (e - p < 1)
251
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse real in pnm image");
252
253
0
  if (*p != '+' && *p != '-' && (*p < '0' || *p > '9'))
254
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "expected numeric field in pnm image");
255
256
0
  while (p < e && (*p == '+' || *p == '-' || *p == '.' || (*p >= '0' && *p <= '9')))
257
0
    p++;
258
259
0
  len = p - orig + 1;
260
0
  end = buf = fz_malloc(ctx, len);
261
262
0
  fz_try(ctx)
263
0
  {
264
0
    memcpy(buf, orig, len - 1);
265
0
    buf[len - 1] = '\0';
266
0
    *number = fz_strtof(buf, &end);
267
0
    p = orig + (end - buf);
268
0
  }
269
0
  fz_always(ctx)
270
0
    fz_free(ctx, buf);
271
0
  fz_catch(ctx)
272
0
    fz_rethrow(ctx);
273
274
0
  return p;
275
0
}
276
277
static const unsigned char *
278
pnm_read_tupletype(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *tupletype)
279
0
{
280
0
  const struct { int len; char *str; int type; } tupletypes[] =
281
0
  {
282
0
    {13, "BLACKANDWHITE", PAM_BW},
283
0
    {19, "BLACKANDWHITE_ALPHA", PAM_BWA},
284
0
    {9, "GRAYSCALE", PAM_GRAY},
285
0
    {15, "GRAYSCALE_ALPHA", PAM_GRAYA},
286
0
    {3, "RGB", PAM_RGB},
287
0
    {9, "RGB_ALPHA", PAM_RGBA},
288
0
    {4, "CMYK", PAM_CMYK},
289
0
    {10, "CMYK_ALPHA", PAM_CMYKA},
290
0
  };
291
0
  const unsigned char *s;
292
0
  int i, len;
293
294
0
  if (e - p < 1)
295
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse tuple type in pnm image");
296
297
0
  s = p;
298
0
  while (p < e && !iswhiteeol(*p))
299
0
    p++;
300
0
  len = p - s;
301
302
0
  for (i = 0; i < (int)nelem(tupletypes); i++)
303
0
    if (len == tupletypes[i].len && !strncmp((char *) s, tupletypes[i].str, len))
304
0
    {
305
0
      *tupletype = tupletypes[i].type;
306
0
      return p;
307
0
    }
308
309
0
  fz_throw(ctx, FZ_ERROR_FORMAT, "unknown tuple type in pnm image");
310
0
}
311
312
static const unsigned char *
313
pnm_read_token(fz_context *ctx, const unsigned char *p, const unsigned char *e, int *token)
314
0
{
315
0
  const struct { int len; char *str; int type; } tokens[] =
316
0
  {
317
0
    {5, "WIDTH", TOKEN_WIDTH},
318
0
    {6, "HEIGHT", TOKEN_HEIGHT},
319
0
    {5, "DEPTH", TOKEN_DEPTH},
320
0
    {6, "MAXVAL", TOKEN_MAXVAL},
321
0
    {8, "TUPLTYPE", TOKEN_TUPLTYPE},
322
0
    {6, "ENDHDR", TOKEN_ENDHDR},
323
0
  };
324
0
  const unsigned char *s;
325
0
  int i, len;
326
327
0
  if (e - p < 1)
328
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot parse header token in pnm image");
329
330
0
  s = p;
331
0
  while (p < e && !iswhiteeol(*p))
332
0
    p++;
333
0
  len = p - s;
334
335
0
  for (i = 0; i < (int)nelem(tokens); i++)
336
0
    if (len == tokens[i].len && !strncmp((char *) s, tokens[i].str, len))
337
0
    {
338
0
      *token = tokens[i].type;
339
0
      return p;
340
0
    }
341
342
0
  fz_throw(ctx, FZ_ERROR_FORMAT, "unknown header token in pnm image");
343
0
}
344
345
static int
346
map_color(fz_context *ctx, int color, int inmax, int outmax)
347
0
{
348
0
  float f = (float) color / inmax;
349
0
  return f * outmax;
350
0
}
351
352
static fz_pixmap *
353
pnm_ascii_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out)
354
0
{
355
0
  fz_pixmap *img = NULL;
356
357
0
  pnm->width = 0;
358
0
  p = pnm_read_comments(ctx, p, e, 1);
359
0
  p = pnm_read_int(ctx, p, e, &pnm->width);
360
0
  p = pnm_read_whites_and_eols(ctx, p, e, 1);
361
362
0
  if (bitmap)
363
0
  {
364
0
    pnm->height = 0;
365
0
    p = pnm_read_int(ctx, p, e, &pnm->height);
366
0
    p = pnm_read_whites_and_eols(ctx, p, e, 1);
367
368
0
    pnm->maxval = 1;
369
0
  }
370
0
  else
371
0
  {
372
0
    pnm->height = 0;
373
0
    p = pnm_read_comments(ctx, p, e, 1);
374
0
    p = pnm_read_int(ctx, p, e, &pnm->height);
375
0
    p = pnm_read_whites_and_eols(ctx, p, e, 1);
376
377
0
    pnm->maxval = 0;
378
0
    p = pnm_read_comments(ctx, p, e, 1);
379
0
    p = pnm_read_int(ctx, p, e, &pnm->maxval);
380
0
    p = pnm_read_white_or_eol(ctx, p, e);
381
0
  }
382
383
0
  if (pnm->maxval <= 0 || pnm->maxval >= 65536)
384
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "maximum sample value of out range in pnm image: %d", pnm->maxval);
385
386
0
  pnm->bitdepth = bitdepth_from_maxval(pnm->maxval);
387
388
0
  if (pnm->height <= 0)
389
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0");
390
0
  if (pnm->width <= 0)
391
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0");
392
0
  if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1))
393
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "image too large");
394
395
0
  if (onlymeta)
396
0
  {
397
0
    int x, y, k;
398
0
    int w, h, n;
399
400
0
    w = pnm->width;
401
0
    h = pnm->height;
402
0
    n = fz_colorspace_n(ctx, pnm->cs);
403
404
0
    if (bitmap)
405
0
    {
406
0
      for (y = 0; y < h; y++)
407
0
        for (x = 0; x < w; x++)
408
0
        {
409
0
          p = pnm_read_whites_and_eols(ctx, p, e, 0);
410
0
          p = pnm_read_digit(ctx, p, e, NULL);
411
0
          p = pnm_read_whites_and_eols(ctx, p, e, 0);
412
0
        }
413
0
    }
414
0
    else
415
0
    {
416
0
      for (y = 0; y < h; y++)
417
0
        for (x = 0; x < w; x++)
418
0
          for (k = 0; k < n; k++)
419
0
          {
420
0
            p = pnm_read_whites_and_eols(ctx, p, e, 0);
421
0
            p = pnm_read_int(ctx, p, e, NULL);
422
0
            p = pnm_read_whites_and_eols(ctx, p, e, 0);
423
0
          }
424
0
    }
425
0
  }
426
0
  else
427
0
  {
428
0
    unsigned char *dp;
429
0
    int x, y, k;
430
0
    int w, h, n;
431
432
0
    img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0);
433
434
0
    fz_try(ctx)
435
0
    {
436
0
      dp = img->samples;
437
438
0
      w = img->w;
439
0
      h = img->h;
440
0
      n = img->n;
441
442
0
      if (bitmap)
443
0
      {
444
0
        for (y = 0; y < h; y++)
445
0
        {
446
0
          for (x = 0; x < w; x++)
447
0
          {
448
0
            int v = 0;
449
0
            p = pnm_read_whites_and_eols(ctx, p, e, 0);
450
0
            p = pnm_read_digit(ctx, p, e, &v);
451
0
            p = pnm_read_whites_and_eols(ctx, p, e, 0);
452
0
            *dp++ = v ? 0x00 : 0xff;
453
0
          }
454
0
        }
455
0
      }
456
0
      else
457
0
      {
458
0
        for (y = 0; y < h; y++)
459
0
          for (x = 0; x < w; x++)
460
0
            for (k = 0; k < n; k++)
461
0
            {
462
0
              int v = 0;
463
0
              p = pnm_read_whites_and_eols(ctx, p, e, 0);
464
0
              p = pnm_read_int(ctx, p, e, &v);
465
0
              p = pnm_read_whites_and_eols(ctx, p, e, 0);
466
0
              v = fz_clampi(v, 0, pnm->maxval);
467
0
              *dp++ = map_color(ctx, v, pnm->maxval, 255);
468
0
            }
469
0
      }
470
0
    }
471
0
    fz_catch(ctx)
472
0
    {
473
0
      fz_drop_pixmap(ctx, img);
474
0
      fz_rethrow(ctx);
475
0
    }
476
0
  }
477
478
0
  if (out)
479
0
    *out = p;
480
481
0
  return img;
482
0
}
483
484
static fz_pixmap *
485
pnm_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out)
486
0
{
487
0
  fz_pixmap *img = NULL;
488
0
  size_t span;
489
0
  int n;
490
491
0
  n = fz_colorspace_n(ctx, pnm->cs);
492
0
  assert(n >= 1 && n <= 3);
493
494
0
  pnm->width = 0;
495
0
  p = pnm_read_comments(ctx, p, e, 1);
496
0
  p = pnm_read_int(ctx, p, e, &pnm->width);
497
0
  p = pnm_read_whites_and_eols(ctx, p, e, 1);
498
499
0
  if (bitmap)
500
0
  {
501
0
    pnm->height = 0;
502
0
    p = pnm_read_int(ctx, p, e, &pnm->height);
503
0
    p = pnm_read_whites_and_eols(ctx, p, e, 1);
504
505
0
    pnm->maxval = 1;
506
0
  }
507
0
  else
508
0
  {
509
0
    pnm->height = 0;
510
0
    p = pnm_read_comments(ctx, p, e, 1);
511
0
    p = pnm_read_int(ctx, p, e, &pnm->height);
512
0
    p = pnm_read_whites_and_eols(ctx, p, e, 1);
513
514
0
    pnm->maxval = 0;
515
0
    p = pnm_read_comments(ctx, p, e, 1);
516
0
    p = pnm_read_int(ctx, p, e, &pnm->maxval);
517
0
    p = pnm_read_white_or_eol(ctx, p, e);
518
0
  }
519
520
0
  if (pnm->maxval <= 0 || pnm->maxval >= 65536)
521
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "maximum sample value of out range in pnm image: %d", pnm->maxval);
522
523
0
  pnm->bitdepth = bitdepth_from_maxval(pnm->maxval);
524
525
0
  if (pnm->height <= 0)
526
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0");
527
0
  if (pnm->width <= 0)
528
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0");
529
0
  if (bitmap)
530
0
  {
531
    /* Overly sensitive test, but we can live with it. */
532
0
    if ((size_t)pnm->width > SIZE_MAX / (unsigned int)n)
533
0
      fz_throw(ctx, FZ_ERROR_LIMIT, "image row too large");
534
0
    span = ((size_t)n * pnm->width + 7)/8;
535
0
  }
536
0
  else
537
0
  {
538
0
    size_t bytes_per_sample = (pnm->bitdepth-1)/8 + 1;
539
0
    span = (size_t)n * bytes_per_sample;
540
0
    if ((size_t)pnm->width > SIZE_MAX / span)
541
0
      fz_throw(ctx, FZ_ERROR_LIMIT, "image row too large");
542
0
    span = (size_t)pnm->width * span;
543
0
  }
544
0
  if ((size_t)pnm->height > SIZE_MAX / span)
545
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "image too large");
546
0
  if (e - p < 0 || ((size_t)(e - p)) < span * (size_t)pnm->height)
547
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "insufficient data");
548
549
0
  if (onlymeta)
550
0
  {
551
0
    p += span * (size_t)pnm->height;
552
0
  }
553
0
  else
554
0
  {
555
0
    unsigned char *dp;
556
0
    int x, y, k;
557
0
    int w, h;
558
559
0
    img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0);
560
561
0
    fz_try(ctx)
562
0
    {
563
0
      dp = img->samples;
564
565
0
      w = img->w;
566
0
      h = img->h;
567
0
      n = img->n;
568
569
0
      if (pnm->maxval == 255)
570
0
      {
571
0
        memcpy(dp, p, (size_t)w * h * n);
572
0
        p += n * w * h;
573
0
      }
574
0
      else if (bitmap)
575
0
      {
576
0
        for (y = 0; y < h; y++)
577
0
        {
578
0
          for (x = 0; x < w; x++)
579
0
          {
580
0
            *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff;
581
0
            if ((x & 0x7) == 7)
582
0
              p++;
583
0
          }
584
0
          if (w & 0x7)
585
0
            p++;
586
0
        }
587
0
      }
588
0
      else if (pnm->maxval < 255)
589
0
      {
590
0
        for (y = 0; y < h; y++)
591
0
          for (x = 0; x < w; x++)
592
0
            for (k = 0; k < n; k++)
593
0
              *dp++ = map_color(ctx, *p++, pnm->maxval, 255);
594
0
      }
595
0
      else
596
0
      {
597
0
        for (y = 0; y < h; y++)
598
0
          for (x = 0; x < w; x++)
599
0
            for (k = 0; k < n; k++)
600
0
            {
601
0
              *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255);
602
0
              p += 2;
603
0
            }
604
0
      }
605
0
    }
606
0
    fz_catch(ctx)
607
0
    {
608
0
      fz_drop_pixmap(ctx, img);
609
0
      fz_rethrow(ctx);
610
0
    }
611
0
  }
612
613
0
  if (out)
614
0
    *out = p;
615
616
0
  return img;
617
0
}
618
619
static const unsigned char *
620
pam_binary_read_header(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e)
621
0
{
622
0
  int token = TOKEN_UNKNOWN;
623
0
  const unsigned char *eol;
624
0
  int seen[TOKEN_ENDHDR] = { 0 };
625
626
0
  pnm->width = 0;
627
0
  pnm->height = 0;
628
0
  pnm->depth = 0;
629
0
  pnm->maxval = 0;
630
0
  pnm->tupletype = 0;
631
632
0
  while (p < e && token != TOKEN_ENDHDR)
633
0
  {
634
0
    eol = pnm_read_until_eol(ctx, p, e, 0);
635
636
0
    p = pnm_read_whites(ctx, p, eol, 0);
637
638
0
    if (p < eol && *p != '#')
639
0
    {
640
0
      p = pnm_read_token(ctx, p, eol, &token);
641
642
0
      if (seen[token - 1])
643
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "token occurs multiple times in pnm image");
644
0
      seen[token - 1] = 1;
645
646
0
      if (token != TOKEN_ENDHDR)
647
0
      {
648
0
        p = pnm_read_whites(ctx, p, eol, 1);
649
0
        switch (token)
650
0
        {
651
0
        case TOKEN_WIDTH: pnm->width = 0; p = pnm_read_int(ctx, p, eol, &pnm->width); break;
652
0
        case TOKEN_HEIGHT: pnm->height = 0; p = pnm_read_int(ctx, p, eol, &pnm->height); break;
653
0
        case TOKEN_DEPTH: pnm->depth = 0; p = pnm_read_int(ctx, p, eol, &pnm->depth); break;
654
0
        case TOKEN_MAXVAL: pnm->maxval = 0; p = pnm_read_int(ctx, p, eol, &pnm->maxval); break;
655
0
        case TOKEN_TUPLTYPE: pnm->tupletype = 0; p = pnm_read_tupletype(ctx, p, eol, &pnm->tupletype); break;
656
0
        }
657
0
      }
658
659
0
      p = pnm_read_whites(ctx, p, eol, 0);
660
0
    }
661
662
0
    if (p < eol && *p == '#')
663
0
      p = pnm_read_comment(ctx, p, eol, 0);
664
665
0
    p = pnm_read_eol(ctx, p, e, 0);
666
0
  }
667
668
0
  return p;
669
0
}
670
671
static fz_pixmap *
672
pam_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, const unsigned char **out)
673
0
{
674
0
  fz_pixmap *img = NULL;
675
0
  int bitmap = 0;
676
0
  int minval = 1;
677
0
  int maxval = 65535;
678
679
0
  fz_var(img);
680
681
0
  p = pam_binary_read_header(ctx, pnm, p, e);
682
683
0
  if (pnm->tupletype == PAM_UNKNOWN)
684
0
    switch (pnm->depth)
685
0
    {
686
0
    case 1: pnm->tupletype = pnm->maxval == 1 ? PAM_BW : PAM_GRAY; break;
687
0
    case 2: pnm->tupletype = pnm->maxval == 1 ? PAM_BWA : PAM_GRAYA; break;
688
0
    case 3: pnm->tupletype = PAM_RGB; break;
689
0
    case 4: pnm->tupletype = PAM_CMYK; break;
690
0
    case 5: pnm->tupletype = PAM_CMYKA; break;
691
0
    default:
692
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "cannot guess tuple type based on depth in pnm image");
693
0
    }
694
695
0
  if (pnm->tupletype == PAM_BW && pnm->maxval > 1)
696
0
    pnm->tupletype = PAM_GRAY;
697
0
  else if (pnm->tupletype == PAM_GRAY && pnm->maxval == 1)
698
0
    pnm->tupletype = PAM_BW;
699
0
  else if (pnm->tupletype == PAM_BWA && pnm->maxval > 1)
700
0
    pnm->tupletype = PAM_GRAYA;
701
0
  else if (pnm->tupletype == PAM_GRAYA && pnm->maxval == 1)
702
0
    pnm->tupletype = PAM_BWA;
703
704
0
  switch (pnm->tupletype)
705
0
  {
706
0
  case PAM_BWA:
707
0
    pnm->alpha = 1;
708
    /* fallthrough */
709
0
  case PAM_BW:
710
0
    pnm->cs = fz_device_gray(ctx);
711
0
    maxval = 1;
712
0
    bitmap = 1;
713
0
    break;
714
0
  case PAM_GRAYA:
715
0
    pnm->alpha = 1;
716
    /* fallthrough */
717
0
  case PAM_GRAY:
718
0
    pnm->cs = fz_device_gray(ctx);
719
0
    minval = 2;
720
0
    break;
721
0
  case PAM_RGBA:
722
0
    pnm->alpha = 1;
723
    /* fallthrough */
724
0
  case PAM_RGB:
725
0
    pnm->cs = fz_device_rgb(ctx);
726
0
    break;
727
0
  case PAM_CMYKA:
728
0
    pnm->alpha = 1;
729
    /* fallthrough */
730
0
  case PAM_CMYK:
731
0
    pnm->cs = fz_device_cmyk(ctx);
732
0
    break;
733
0
  default:
734
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "unsupported tuple type");
735
0
  }
736
737
0
  if (pnm->depth != fz_colorspace_n(ctx, pnm->cs) + pnm->alpha)
738
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "depth out of tuple type range");
739
0
  if (pnm->maxval < minval || pnm->maxval > maxval)
740
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "maxval out of range");
741
742
0
  pnm->bitdepth = bitdepth_from_maxval(pnm->maxval);
743
744
0
  if (pnm->height <= 0)
745
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0");
746
0
  if (pnm->width <= 0)
747
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0");
748
0
  if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1))
749
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "image too large");
750
751
0
  if (onlymeta)
752
0
  {
753
0
    int packed;
754
0
    int w, h, n;
755
0
    size_t size;
756
757
0
    w = pnm->width;
758
0
    h = pnm->height;
759
0
    n = fz_colorspace_n(ctx, pnm->cs) + pnm->alpha;
760
761
    /* some encoders incorrectly pack bits into bytes and invert the image */
762
0
    packed = 0;
763
0
    size = (size_t)w * h * n;
764
0
    if (pnm->maxval == 1)
765
0
    {
766
0
      const unsigned char *e_packed = p + size / 8;
767
0
      if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7')
768
0
        e = e_packed;
769
0
      if (e < p || (size_t)(e - p) < size)
770
0
        packed = 1;
771
0
    }
772
0
    if (packed && (e < p || (size_t)(e - p) < size / 8))
773
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "truncated packed image");
774
0
    if (!packed && (e < p || (size_t)(e - p) < size * (pnm->maxval < 256 ? 1 : 2)))
775
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image");
776
777
0
    if (pnm->maxval == 255)
778
0
      p += size;
779
0
    else if (bitmap && packed)
780
0
      p += ((w + 7) / 8) * h;
781
0
    else if (bitmap)
782
0
      p += size;
783
0
    else if (pnm->maxval < 255)
784
0
      p += size;
785
0
    else
786
0
      p += 2 * size;
787
0
  }
788
0
  else
789
0
  {
790
0
    unsigned char *dp;
791
0
    int x, y, k, packed;
792
0
    int w, h, n;
793
0
    size_t size;
794
795
0
    img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, pnm->alpha);
796
0
    fz_try(ctx)
797
0
    {
798
0
      dp = img->samples;
799
800
0
      w = img->w;
801
0
      h = img->h;
802
0
      n = img->n;
803
804
      /* some encoders incorrectly pack bits into bytes and invert the image */
805
0
      size = (size_t)w * h * n;
806
0
      packed = 0;
807
0
      if (pnm->maxval == 1)
808
0
      {
809
0
        const unsigned char *e_packed = p + size / 8;
810
0
        if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7')
811
0
          e = e_packed;
812
0
        if (e < p || (size_t)(e - p) < size)
813
0
          packed = 1;
814
0
      }
815
0
      if (packed && (e < p || (size_t)(e - p) < size / 8))
816
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "truncated packed image");
817
0
      if (!packed && (e < p || (size_t)(e - p) < size * (pnm->maxval < 256 ? 1 : 2)))
818
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image");
819
820
0
      if (pnm->maxval == 255)
821
0
        memcpy(dp, p, size);
822
0
      else if (bitmap && packed)
823
0
      {
824
0
        for (y = 0; y < h; y++)
825
0
          for (x = 0; x < w; x++)
826
0
          {
827
0
            for (k = 0; k < n; k++)
828
0
            {
829
0
              *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff;
830
0
              if ((x & 0x7) == 7)
831
0
                p++;
832
0
            }
833
0
            if (w & 0x7)
834
0
              p++;
835
0
          }
836
0
      }
837
0
      else if (bitmap)
838
0
      {
839
0
        for (y = 0; y < h; y++)
840
0
          for (x = 0; x < w; x++)
841
0
            for (k = 0; k < n; k++)
842
0
              *dp++ = *p++ ? 0xff : 0x00;
843
0
      }
844
0
      else if (pnm->maxval < 255)
845
0
      {
846
0
        for (y = 0; y < h; y++)
847
0
          for (x = 0; x < w; x++)
848
0
            for (k = 0; k < n; k++)
849
0
              *dp++ = map_color(ctx, *p++, pnm->maxval, 255);
850
0
      }
851
0
      else
852
0
      {
853
0
        for (y = 0; y < h; y++)
854
0
          for (x = 0; x < w; x++)
855
0
            for (k = 0; k < n; k++)
856
0
            {
857
0
              *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255);
858
0
              p += 2;
859
0
            }
860
0
      }
861
862
0
      if (pnm->alpha)
863
0
        fz_premultiply_pixmap(ctx, img);
864
0
    }
865
0
    fz_catch(ctx)
866
0
    {
867
0
      fz_drop_pixmap(ctx, img);
868
0
      fz_rethrow(ctx);
869
0
    }
870
0
  }
871
872
0
  if (out)
873
0
    *out = p;
874
875
0
  return img;
876
0
}
877
878
static const unsigned char *
879
pfm_binary_read_header(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e)
880
0
{
881
0
  pnm->width = 0;
882
0
  p = pnm_read_int(ctx, p, e, &pnm->width);
883
0
  p = pnm_read_whites_and_eols(ctx, p, e,1);
884
885
0
  pnm->height = 0;
886
0
  p = pnm_read_int(ctx, p, e, &pnm->height);
887
0
  p = pnm_read_whites_and_eols(ctx, p, e,1);
888
889
0
  p = pnm_read_real(ctx, p, e, &pnm->scale);
890
891
0
  p = pnm_read_white_or_eol(ctx, p, e);
892
893
0
  if (pnm->scale >= 0)
894
0
    pnm->endian = ENDIAN_BIG;
895
0
  else
896
0
  {
897
0
    pnm->endian = ENDIAN_LITTLE;
898
0
    pnm->scale = -pnm->scale;
899
0
  }
900
901
0
  return p;
902
0
}
903
904
static fz_pixmap *
905
pfm_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int rgb, const unsigned char **out)
906
0
{
907
0
  fz_pixmap *pix = NULL;
908
909
0
  fz_var(pix);
910
911
0
  p = pfm_binary_read_header(ctx, pnm, p, e);
912
0
  pnm->cs = rgb ? fz_device_rgb(ctx) : fz_device_gray(ctx);
913
914
0
  if (pnm->height <= 0)
915
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0");
916
0
  if (pnm->width <= 0)
917
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0");
918
0
  if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1))
919
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "image too large");
920
921
0
  if (onlymeta)
922
0
  {
923
0
    size_t w = pnm->width;
924
0
    size_t h = pnm->height;
925
0
    int n = fz_colorspace_n(ctx, pnm->cs);
926
0
    size_t size = w * h * n * sizeof(float);
927
928
0
    if (e < p || (size_t)(e - p) < size)
929
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image");
930
931
0
    p += size;
932
0
  }
933
0
  else
934
0
  {
935
0
    float *samples = NULL;
936
0
    float *sample;
937
0
    int w = pnm->width;
938
0
    int h = pnm->height;
939
0
    int n = fz_colorspace_n(ctx, pnm->cs);
940
0
    size_t size = (size_t) w * h * n * sizeof(float);
941
0
    int x, y, k;
942
943
0
    if (e < p || (size_t)(e - p) < size)
944
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "truncated image");
945
946
0
    sample = samples = fz_malloc(ctx, size);
947
0
    fz_try(ctx)
948
0
    {
949
0
      for (y = 0; y < h; y++)
950
0
        for (x = 0; x < w; x++)
951
0
          for (k = 0; k < n; k++)
952
0
          {
953
0
            uint32_t u;
954
0
            float f;
955
956
0
            if (pnm->endian == ENDIAN_LITTLE)
957
0
              u = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
958
0
            else
959
0
              u = p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24);
960
0
            memcpy(&f, &u, sizeof(float));
961
962
0
            *sample++ = f / pnm->scale;
963
0
            p += sizeof(float);
964
0
          }
965
966
0
      pix = fz_new_pixmap_from_float_data(ctx, pnm->cs, w, h, samples);
967
0
    }
968
0
    fz_always(ctx)
969
0
      fz_free(ctx, samples);
970
0
    fz_catch(ctx)
971
0
      fz_rethrow(ctx);
972
0
  }
973
974
0
  if (out)
975
0
    *out = p;
976
977
0
  return pix;
978
0
}
979
980
static fz_pixmap *
981
pnm_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, size_t total, int onlymeta, int subimage)
982
0
{
983
0
  const unsigned char *e = p + total;
984
0
  char signature[3] = { 0 };
985
0
  fz_pixmap *pix = NULL;
986
987
0
  while (p < e && ((!onlymeta && subimage >= 0) || onlymeta))
988
0
  {
989
0
    int subonlymeta = onlymeta || (subimage > 0);
990
991
0
    p = pnm_read_whites_and_eols(ctx, p, e, 0);
992
0
    p = pnm_read_signature(ctx, p, e, signature);
993
0
    p = pnm_read_whites_and_eols(ctx, p, e, 1);
994
995
0
    if (!strcmp(signature, "P1"))
996
0
    {
997
0
      pnm->cs = fz_device_gray(ctx);
998
0
      pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 1, &p);
999
0
    }
1000
0
    else if (!strcmp(signature, "P2"))
1001
0
    {
1002
0
      pnm->cs = fz_device_gray(ctx);
1003
0
      pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
1004
0
    }
1005
0
    else if (!strcmp(signature, "P3"))
1006
0
    {
1007
0
      pnm->cs = fz_device_rgb(ctx);
1008
0
      pix = pnm_ascii_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
1009
0
    }
1010
0
    else if (!strcmp(signature, "P4"))
1011
0
    {
1012
0
      pnm->cs = fz_device_gray(ctx);
1013
0
      pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 1, &p);
1014
0
    }
1015
0
    else if (!strcmp(signature, "P5"))
1016
0
    {
1017
0
      pnm->cs = fz_device_gray(ctx);
1018
0
      pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
1019
0
    }
1020
0
    else if (!strcmp(signature, "P6"))
1021
0
    {
1022
0
      pnm->cs = fz_device_rgb(ctx);
1023
0
      pix = pnm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
1024
0
    }
1025
0
    else if (!strcmp(signature, "P7"))
1026
0
      pix = pam_binary_read_image(ctx, pnm, p, e, subonlymeta, &p);
1027
0
    else if (!strcmp(signature, "Pf"))
1028
0
      pix = pfm_binary_read_image(ctx, pnm, p, e, subonlymeta, 0, &p);
1029
0
    else if (!strcmp(signature, "PF"))
1030
0
      pix = pfm_binary_read_image(ctx, pnm, p, e, subonlymeta, 1, &p);
1031
0
    else
1032
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "unsupported portable anymap signature (0x%02x, 0x%02x)", signature[0], signature[1]);
1033
1034
0
    p = pnm_read_whites_and_eols(ctx, p, e, 0);
1035
1036
0
    if (onlymeta)
1037
0
      pnm->subimages++;
1038
0
    if (subimage >= 0)
1039
0
      subimage--;
1040
0
  }
1041
1042
0
  if (p >= e && subimage >= 0)
1043
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "subimage count out of range");
1044
1045
0
  return pix;
1046
0
}
1047
1048
fz_pixmap *
1049
fz_load_pnm(fz_context *ctx, const unsigned char *p, size_t total)
1050
0
{
1051
0
  struct info pnm = { 0 };
1052
0
  return pnm_read_image(ctx, &pnm, p, total, 0, 0);
1053
0
}
1054
1055
void
1056
fz_load_pnm_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
1057
0
{
1058
0
  struct info pnm = { 0 };
1059
0
  (void) pnm_read_image(ctx, &pnm, p, total, 1, 0);
1060
0
  *cspacep = fz_keep_colorspace(ctx, pnm.cs); /* pnm.cs is a borrowed device colorspace */
1061
0
  *wp = pnm.width;
1062
0
  *hp = pnm.height;
1063
0
  *xresp = 72;
1064
0
  *yresp = 72;
1065
0
}
1066
1067
fz_pixmap *
1068
fz_load_pnm_subimage(fz_context *ctx, const unsigned char *p, size_t total, int subimage)
1069
0
{
1070
0
  struct info pnm = { 0 };
1071
0
  return pnm_read_image(ctx, &pnm, p, total, 0, subimage);
1072
0
}
1073
1074
int
1075
fz_load_pnm_subimage_count(fz_context *ctx, const unsigned char *p, size_t total)
1076
0
{
1077
0
  struct info pnm = { 0 };
1078
0
  (void) pnm_read_image(ctx, &pnm, p, total, 1, -1);
1079
0
  return pnm.subimages;
1080
0
}