Coverage Report

Created: 2026-03-15 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/CMap.cc
Line
Count
Source
1
//========================================================================
2
//
3
// CMap.cc
4
//
5
// Copyright 2001-2003 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <ctype.h>
15
#include "gmem.h"
16
#include "gmempp.h"
17
#include "gfile.h"
18
#include "GString.h"
19
#include "GHash.h"
20
#include "Error.h"
21
#include "GlobalParams.h"
22
#include "PSTokenizer.h"
23
#include "Object.h"
24
#include "Stream.h"
25
#include "CMap.h"
26
27
//------------------------------------------------------------------------
28
29
struct CMapVectorEntry {
30
  GBool isVector;
31
  union {
32
    CMapVectorEntry *vector;
33
    CID cid;
34
  };
35
};
36
37
//------------------------------------------------------------------------
38
39
0
static int getCharFromFile(void *data) {
40
0
  return fgetc((FILE *)data);
41
0
}
42
43
13.2M
static int getCharFromStream(void *data) {
44
13.2M
  return ((Stream *)data)->getChar();
45
13.2M
}
46
47
//------------------------------------------------------------------------
48
49
CMap *CMap::parse(CMapCache *cache, GString *collectionA, Object *obj,
50
19.4k
      GHash *usedCMaps) {
51
19.4k
  CMap *cMap;
52
19.4k
  GString *cMapNameA;
53
54
19.4k
  if (obj->isName()) {
55
7.29k
    cMapNameA = new GString(obj->getName());
56
7.29k
    if (!(cMap = globalParams->getCMap(collectionA, cMapNameA))) {
57
247
      error(errSyntaxError, -1,
58
247
      "Unknown CMap '{0:t}' for character collection '{1:t}'",
59
247
      cMapNameA, collectionA);
60
247
    }
61
7.29k
    delete cMapNameA;
62
12.1k
  } else if (obj->isStream()) {
63
10.6k
    GHash *newUsedCMaps = NULL;
64
10.6k
    if (!usedCMaps) {
65
10.6k
      usedCMaps = newUsedCMaps = new GHash(gTrue);
66
10.6k
    }
67
10.6k
    if (!(cMap = CMap::parse(NULL, collectionA, obj->getStream(),
68
10.6k
           usedCMaps))) {
69
0
      error(errSyntaxError, -1, "Invalid CMap in Type 0 font");
70
0
    }
71
10.6k
    if (newUsedCMaps) {
72
10.6k
      delete newUsedCMaps;
73
10.6k
    }
74
10.6k
  } else {
75
1.52k
    error(errSyntaxError, -1, "Invalid Encoding in Type 0 font");
76
1.52k
    return NULL;
77
1.52k
  }
78
17.9k
  return cMap;
79
19.4k
}
80
81
CMap *CMap::parse(CMapCache *cache, GString *collectionA,
82
1.65k
      GString *cMapNameA, GHash *usedCMaps) {
83
1.65k
  FILE *f;
84
1.65k
  CMap *cMap;
85
86
1.65k
  if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
87
88
    // Check for an identity CMap.
89
1.65k
    if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
90
1.38k
      return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
91
1.38k
    }
92
268
    if (!cMapNameA->cmp("Identity-V")) {
93
21
      return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
94
21
    }
95
96
247
    error(errSyntaxError, -1,
97
247
    "Couldn't find '{0:t}' CMap file for '{1:t}' collection",
98
247
    cMapNameA, collectionA);
99
247
    return NULL;
100
268
  }
101
102
0
  GHash *newUsedCMaps = NULL;
103
0
  if (!usedCMaps) {
104
0
    usedCMaps = newUsedCMaps = new GHash(gTrue);
105
0
  }
106
107
0
  cMap = new CMap(collectionA->copy(), cMapNameA->copy());
108
0
  cMap->parse2(cache, &getCharFromFile, f, usedCMaps);
109
110
0
  if (newUsedCMaps) {
111
0
    delete newUsedCMaps;
112
0
  }
113
114
0
  fclose(f);
115
116
0
  return cMap;
117
1.65k
}
118
119
CMap *CMap::parse(CMapCache *cache, GString *collectionA, Stream *str,
120
10.6k
      GHash *usedCMaps) {
121
10.6k
  Object obj1;
122
10.6k
  CMap *cMap;
123
124
  // check for a loop
125
10.6k
  if (usedCMaps) {
126
10.6k
    GString *name;
127
10.6k
    if (str->getDict()->lookup("CMapName", &obj1)->isName()) {
128
20
      name = new GString(obj1.getName());
129
10.6k
    } else {
130
10.6k
      name = new GString();
131
10.6k
    }
132
10.6k
    obj1.free();
133
10.6k
    if (usedCMaps->lookupInt(name)) {
134
0
      error(errSyntaxError, -1, "Loop in usecmap");
135
0
      delete name;
136
0
      return NULL;
137
0
    }
138
10.6k
    usedCMaps->add(name, 1);
139
10.6k
  }
140
141
10.6k
  cMap = new CMap(collectionA->copy(), NULL);
142
143
10.6k
  if (!str->getDict()->lookup("UseCMap", &obj1)->isNull()) {
144
0
    cMap->useCMap(cache, &obj1, usedCMaps);
145
0
  }
146
10.6k
  obj1.free();
147
148
10.6k
  str->reset();
149
10.6k
  cMap->parse2(cache, &getCharFromStream, str, usedCMaps);
150
10.6k
  str->close();
151
10.6k
  return cMap;
152
10.6k
}
153
154
void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data,
155
10.6k
      GHash *usedCMaps) {
156
10.6k
  PSTokenizer *pst;
157
10.6k
  char tok1[256], tok2[256], tok3[256];
158
10.6k
  int n1, n2, n3;
159
10.6k
  Guint start, end, code;
160
161
10.6k
  pst = new PSTokenizer(getCharFunc, data);
162
10.6k
  pst->getToken(tok1, sizeof(tok1), &n1);
163
591k
  while (pst->getToken(tok2, sizeof(tok2), &n2)) {
164
580k
    if (!strcmp(tok2, "usecmap")) {
165
0
      if (tok1[0] == '/') {
166
0
  useCMap(cache, tok1 + 1, usedCMaps);
167
0
      }
168
0
      pst->getToken(tok1, sizeof(tok1), &n1);
169
580k
    } else if (!strcmp(tok1, "/WMode")) {
170
208
      wMode = atoi(tok2);
171
208
      pst->getToken(tok1, sizeof(tok1), &n1);
172
580k
    } else if (!strcmp(tok2, "begincidchar")) {
173
6.55k
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
174
6.54k
  if (!strcmp(tok1, "endcidchar")) {
175
44
    break;
176
44
  }
177
6.50k
  if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
178
6.47k
      !strcmp(tok2, "endcidchar")) {
179
42
    error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
180
42
    break;
181
42
  }
182
6.46k
  if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
183
3.87k
        n1 >= 4 && (n1 & 1) == 0)) {
184
2.73k
    error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
185
2.73k
    continue;
186
2.73k
  }
187
3.73k
  tok1[n1 - 1] = '\0';
188
3.73k
  if (sscanf(tok1 + 1, "%x", &code) != 1) {
189
127
    error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
190
127
    continue;
191
127
  }
192
3.60k
  n1 = (n1 - 2) / 2;
193
3.60k
  addCIDs(code, code, n1, (CID)atoi(tok2));
194
3.60k
      }
195
98
      pst->getToken(tok1, sizeof(tok1), &n1);
196
580k
    } else if (!strcmp(tok2, "begincidrange")) {
197
3.93k
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
198
3.90k
  if (!strcmp(tok1, "endcidrange")) {
199
21
    break;
200
21
  }
201
3.88k
  if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
202
3.86k
      !strcmp(tok2, "endcidrange") ||
203
3.85k
      !pst->getToken(tok3, sizeof(tok3), &n3) ||
204
3.83k
      !strcmp(tok3, "endcidrange")) {
205
60
    error(errSyntaxError, -1, "Illegal entry in cidrange block in CMap");
206
60
    break;
207
60
  }
208
3.82k
  if (tok1[0] == '<' && tok2[0] == '<' &&
209
2.38k
      n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
210
2.14k
    tok1[n1 - 1] = tok2[n1 - 1] = '\0';
211
2.14k
    sscanf(tok1 + 1, "%x", &start);
212
2.14k
    sscanf(tok2 + 1, "%x", &end);
213
2.14k
    n1 = (n1 - 2) / 2;
214
2.14k
    addCIDs(start, end, n1, (CID)atoi(tok3));
215
2.14k
  }
216
3.82k
      }
217
104
      pst->getToken(tok1, sizeof(tok1), &n1);
218
580k
    } else {
219
580k
      strcpy(tok1, tok2);
220
580k
    }
221
580k
  }
222
10.6k
  delete pst;
223
10.6k
}
224
225
10.6k
CMap::CMap(GString *collectionA, GString *cMapNameA) {
226
10.6k
  int i;
227
228
10.6k
  collection = collectionA;
229
10.6k
  cMapName = cMapNameA;
230
10.6k
  isIdent = gFalse;
231
10.6k
  wMode = 0;
232
10.6k
  vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
233
2.74M
  for (i = 0; i < 256; ++i) {
234
2.73M
    vector[i].isVector = gFalse;
235
2.73M
    vector[i].cid = 0;
236
2.73M
  }
237
10.6k
  refCnt = 1;
238
10.6k
}
239
240
1.41k
CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
241
1.41k
  collection = collectionA;
242
1.41k
  cMapName = cMapNameA;
243
1.41k
  isIdent = gTrue;
244
1.41k
  wMode = wModeA;
245
1.41k
  vector = NULL;
246
1.41k
  refCnt = 1;
247
1.41k
}
248
249
0
void CMap::useCMap(CMapCache *cache, char *useName, GHash *usedCMaps) {
250
0
  GString *useNameStr;
251
0
  CMap *subCMap;
252
253
  // check for a loop
254
0
  if (usedCMaps) {
255
0
    if (usedCMaps->lookupInt(useName)) {
256
0
      error(errSyntaxError, -1, "Loop in usecmap");
257
0
      return;
258
0
    }
259
0
    usedCMaps->add(new GString(useName), 1);
260
0
  }
261
262
0
  useNameStr = new GString(useName);
263
  // if cache is non-NULL, we already have a lock, and we can use
264
  // CMapCache::getCMap() directly; otherwise, we need to use
265
  // GlobalParams::getCMap() in order to acqure the lock
266
0
  if (cache) {
267
0
    subCMap = cache->getCMap(collection, useNameStr, usedCMaps);
268
0
  } else {
269
0
    subCMap = globalParams->getCMap(collection, useNameStr);
270
0
  }
271
0
  delete useNameStr;
272
0
  if (!subCMap) {
273
0
    return;
274
0
  }
275
0
  isIdent = subCMap->isIdent;
276
0
  if (subCMap->vector) {
277
0
    copyVector(vector, subCMap->vector);
278
0
  }
279
0
  subCMap->decRefCnt();
280
0
}
281
282
0
void CMap::useCMap(CMapCache *cache, Object *obj, GHash *usedCMaps) {
283
0
  CMap *subCMap;
284
285
0
  subCMap = CMap::parse(cache, collection, obj, usedCMaps);
286
0
  if (!subCMap) {
287
0
    return;
288
0
  }
289
0
  isIdent = subCMap->isIdent;
290
0
  if (subCMap->vector) {
291
0
    copyVector(vector, subCMap->vector);
292
0
  }
293
0
  subCMap->decRefCnt();
294
0
}
295
296
0
void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
297
0
  int i, j;
298
299
0
  for (i = 0; i < 256; ++i) {
300
0
    if (src[i].isVector) {
301
0
      if (!dest[i].isVector) {
302
0
  dest[i].isVector = gTrue;
303
0
  dest[i].vector =
304
0
    (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
305
0
  for (j = 0; j < 256; ++j) {
306
0
    dest[i].vector[j].isVector = gFalse;
307
0
    dest[i].vector[j].cid = 0;
308
0
  }
309
0
      }
310
0
      copyVector(dest[i].vector, src[i].vector);
311
0
    } else {
312
0
      if (dest[i].isVector) {
313
0
  error(errSyntaxError, -1, "Collision in usecmap");
314
0
      } else {
315
0
  dest[i].cid = src[i].cid;
316
0
      }
317
0
    }
318
0
  }
319
0
}
320
321
5.74k
void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
322
5.74k
  CMapVectorEntry *vec;
323
5.74k
  int byte, byte0, byte1;
324
5.74k
  Guint start1, end1, i, j, k;
325
326
5.74k
  start1 = start & 0xffffff00;
327
5.74k
  end1 = end & 0xffffff00;
328
12.7k
  for (i = start1; i <= end1; i += 0x100) {
329
6.98k
    vec = vector;
330
14.4k
    for (j = nBytes - 1; j >= 1; --j) {
331
7.48k
      byte = (i >> (8 * j)) & 0xff;
332
7.48k
      if (!vec[byte].isVector) {
333
1.96k
  vec[byte].isVector = gTrue;
334
1.96k
  vec[byte].vector =
335
1.96k
      (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
336
505k
  for (k = 0; k < 256; ++k) {
337
503k
    vec[byte].vector[k].isVector = gFalse;
338
503k
    vec[byte].vector[k].cid = 0;
339
503k
  }
340
1.96k
      }
341
7.48k
      vec = vec[byte].vector;
342
7.48k
    }
343
6.98k
    byte0 = (i < start) ? (start & 0xff) : 0;
344
6.98k
    byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff;
345
353k
    for (byte = byte0; byte <= byte1; ++byte) {
346
346k
      if (vec[byte].isVector) {
347
524
  error(errSyntaxError, -1, "Invalid CID ({0:x} [{1:d} bytes]) in CMap",
348
524
        i, nBytes);
349
345k
      } else {
350
345k
  vec[byte].cid = firstCID + ((i + byte) - start);
351
345k
      }
352
346k
    }
353
6.98k
  }
354
5.74k
}
355
356
12.0k
CMap::~CMap() {
357
12.0k
  delete collection;
358
12.0k
  if (cMapName) {
359
1.40k
    delete cMapName;
360
1.40k
  }
361
12.0k
  if (vector) {
362
10.6k
    freeCMapVector(vector);
363
10.6k
  }
364
12.0k
}
365
366
12.6k
void CMap::freeCMapVector(CMapVectorEntry *vec) {
367
12.6k
  int i;
368
369
3.24M
  for (i = 0; i < 256; ++i) {
370
3.23M
    if (vec[i].isVector) {
371
1.96k
      freeCMapVector(vec[i].vector);
372
1.96k
    }
373
3.23M
  }
374
12.6k
  gfree(vec);
375
12.6k
}
376
377
7.05k
void CMap::incRefCnt() {
378
7.05k
#if MULTITHREADED
379
7.05k
  gAtomicIncrement(&refCnt);
380
#else
381
  ++refCnt;
382
#endif
383
7.05k
}
384
385
19.1k
void CMap::decRefCnt() {
386
19.1k
  GBool done;
387
388
19.1k
#if MULTITHREADED
389
19.1k
  done = gAtomicDecrement(&refCnt) == 0;
390
#else
391
  done = --refCnt == 0;
392
#endif
393
19.1k
  if (done) {
394
12.0k
    delete this;
395
12.0k
  }
396
19.1k
}
397
398
5.66k
GBool CMap::match(GString *collectionA, GString *cMapNameA) {
399
5.66k
  return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
400
5.66k
}
401
402
208k
CID CMap::getCID(char *s, int len, CharCode *c, int *nUsed) {
403
208k
  CMapVectorEntry *vec;
404
208k
  CharCode cc;
405
208k
  int n, i;
406
407
208k
  vec = vector;
408
208k
  cc = 0;
409
208k
  n = 0;
410
208k
  while (vec && n < len) {
411
35.7k
    i = s[n++] & 0xff;
412
35.7k
    cc = (cc << 8) | i;
413
35.7k
    if (!vec[i].isVector) {
414
35.2k
      *c = cc;
415
35.2k
      *nUsed = n;
416
35.2k
      return vec[i].cid;
417
35.2k
    }
418
468
    vec = vec[i].vector;
419
468
  }
420
172k
  if (isIdent && len >= 2) {
421
    // identity CMap
422
166k
    *nUsed = 2;
423
166k
    *c = cc = ((s[0] & 0xff) << 8) + (s[1] & 0xff);
424
166k
    return cc;
425
166k
  }
426
6.17k
  *nUsed = 1;
427
6.17k
  *c = s[0] & 0xff;
428
6.17k
  return 0;
429
172k
}
430
431
//------------------------------------------------------------------------
432
433
21.8k
CMapCache::CMapCache() {
434
21.8k
  int i;
435
436
109k
  for (i = 0; i < cMapCacheSize; ++i) {
437
87.3k
    cache[i] = NULL;
438
87.3k
  }
439
21.8k
}
440
441
21.8k
CMapCache::~CMapCache() {
442
21.8k
  int i;
443
444
109k
  for (i = 0; i < cMapCacheSize; ++i) {
445
87.3k
    if (cache[i]) {
446
1.41k
      cache[i]->decRefCnt();
447
1.41k
    }
448
87.3k
  }
449
21.8k
}
450
451
CMap *CMapCache::getCMap(GString *collection, GString *cMapName,
452
7.29k
       GHash *usedCMaps) {
453
7.29k
  CMap *cmap;
454
7.29k
  int i, j;
455
456
7.29k
  if (cache[0] && cache[0]->match(collection, cMapName)) {
457
5.64k
    cache[0]->incRefCnt();
458
5.64k
    return cache[0];
459
5.64k
  }
460
6.62k
  for (i = 1; i < cMapCacheSize; ++i) {
461
4.97k
    if (cache[i] && cache[i]->match(collection, cMapName)) {
462
0
      cmap = cache[i];
463
0
      for (j = i; j >= 1; --j) {
464
0
  cache[j] = cache[j - 1];
465
0
      }
466
0
      cache[0] = cmap;
467
0
      cmap->incRefCnt();
468
0
      return cmap;
469
0
    }
470
4.97k
  }
471
1.65k
  if ((cmap = CMap::parse(this, collection, cMapName, usedCMaps))) {
472
1.41k
    if (cache[cMapCacheSize - 1]) {
473
0
      cache[cMapCacheSize - 1]->decRefCnt();
474
0
    }
475
5.64k
    for (j = cMapCacheSize - 1; j >= 1; --j) {
476
4.23k
      cache[j] = cache[j - 1];
477
4.23k
    }
478
1.41k
    cache[0] = cmap;
479
1.41k
    cmap->incRefCnt();
480
1.41k
    return cmap;
481
1.41k
  }
482
247
  return NULL;
483
1.65k
}