Coverage Report

Created: 2026-03-31 07:04

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
11.5M
static int getCharFromStream(void *data) {
44
11.5M
  return ((Stream *)data)->getChar();
45
11.5M
}
46
47
//------------------------------------------------------------------------
48
49
CMap *CMap::parse(CMapCache *cache, GString *collectionA, Object *obj,
50
12.3k
      GHash *usedCMaps) {
51
12.3k
  CMap *cMap;
52
12.3k
  GString *cMapNameA;
53
54
12.3k
  if (obj->isName()) {
55
5.64k
    cMapNameA = new GString(obj->getName());
56
5.64k
    if (!(cMap = globalParams->getCMap(collectionA, cMapNameA))) {
57
447
      error(errSyntaxError, -1,
58
447
      "Unknown CMap '{0:t}' for character collection '{1:t}'",
59
447
      cMapNameA, collectionA);
60
447
    }
61
5.64k
    delete cMapNameA;
62
6.71k
  } else if (obj->isStream()) {
63
5.39k
    GHash *newUsedCMaps = NULL;
64
5.39k
    if (!usedCMaps) {
65
5.39k
      usedCMaps = newUsedCMaps = new GHash(gTrue);
66
5.39k
    }
67
5.39k
    if (!(cMap = CMap::parse(NULL, collectionA, obj->getStream(),
68
5.39k
           usedCMaps))) {
69
0
      error(errSyntaxError, -1, "Invalid CMap in Type 0 font");
70
0
    }
71
5.39k
    if (newUsedCMaps) {
72
5.39k
      delete newUsedCMaps;
73
5.39k
    }
74
5.39k
  } else {
75
1.32k
    error(errSyntaxError, -1, "Invalid Encoding in Type 0 font");
76
1.32k
    return NULL;
77
1.32k
  }
78
11.0k
  return cMap;
79
12.3k
}
80
81
CMap *CMap::parse(CMapCache *cache, GString *collectionA,
82
1.75k
      GString *cMapNameA, GHash *usedCMaps) {
83
1.75k
  FILE *f;
84
1.75k
  CMap *cMap;
85
86
1.75k
  if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
87
88
    // Check for an identity CMap.
89
1.75k
    if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
90
1.28k
      return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
91
1.28k
    }
92
466
    if (!cMapNameA->cmp("Identity-V")) {
93
19
      return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
94
19
    }
95
96
447
    error(errSyntaxError, -1,
97
447
    "Couldn't find '{0:t}' CMap file for '{1:t}' collection",
98
447
    cMapNameA, collectionA);
99
447
    return NULL;
100
466
  }
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.75k
}
118
119
CMap *CMap::parse(CMapCache *cache, GString *collectionA, Stream *str,
120
5.39k
      GHash *usedCMaps) {
121
5.39k
  Object obj1;
122
5.39k
  CMap *cMap;
123
124
  // check for a loop
125
5.39k
  if (usedCMaps) {
126
5.39k
    GString *name;
127
5.39k
    if (str->getDict()->lookup("CMapName", &obj1)->isName()) {
128
21
      name = new GString(obj1.getName());
129
5.37k
    } else {
130
5.37k
      name = new GString();
131
5.37k
    }
132
5.39k
    obj1.free();
133
5.39k
    if (usedCMaps->lookupInt(name)) {
134
0
      error(errSyntaxError, -1, "Loop in usecmap");
135
0
      delete name;
136
0
      return NULL;
137
0
    }
138
5.39k
    usedCMaps->add(name, 1);
139
5.39k
  }
140
141
5.39k
  cMap = new CMap(collectionA->copy(), NULL);
142
143
5.39k
  if (!str->getDict()->lookup("UseCMap", &obj1)->isNull()) {
144
0
    cMap->useCMap(cache, &obj1, usedCMaps);
145
0
  }
146
5.39k
  obj1.free();
147
148
5.39k
  str->reset();
149
5.39k
  cMap->parse2(cache, &getCharFromStream, str, usedCMaps);
150
5.39k
  str->close();
151
5.39k
  return cMap;
152
5.39k
}
153
154
void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data,
155
5.39k
      GHash *usedCMaps) {
156
5.39k
  PSTokenizer *pst;
157
5.39k
  char tok1[256], tok2[256], tok3[256];
158
5.39k
  int n1, n2, n3;
159
5.39k
  Guint start, end, code;
160
161
5.39k
  pst = new PSTokenizer(getCharFunc, data);
162
5.39k
  pst->getToken(tok1, sizeof(tok1), &n1);
163
383k
  while (pst->getToken(tok2, sizeof(tok2), &n2)) {
164
377k
    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
377k
    } else if (!strcmp(tok1, "/WMode")) {
170
193
      wMode = atoi(tok2);
171
193
      pst->getToken(tok1, sizeof(tok1), &n1);
172
377k
    } else if (!strcmp(tok2, "begincidchar")) {
173
6.82k
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
174
6.81k
  if (!strcmp(tok1, "endcidchar")) {
175
39
    break;
176
39
  }
177
6.77k
  if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
178
6.74k
      !strcmp(tok2, "endcidchar")) {
179
41
    error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
180
41
    break;
181
41
  }
182
6.73k
  if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
183
4.05k
        n1 >= 4 && (n1 & 1) == 0)) {
184
2.83k
    error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
185
2.83k
    continue;
186
2.83k
  }
187
3.89k
  tok1[n1 - 1] = '\0';
188
3.89k
  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.76k
  n1 = (n1 - 2) / 2;
193
3.76k
  addCIDs(code, code, n1, (CID)atoi(tok2));
194
3.76k
      }
195
95
      pst->getToken(tok1, sizeof(tok1), &n1);
196
377k
    } else if (!strcmp(tok2, "begincidrange")) {
197
3.13k
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
198
3.12k
  if (!strcmp(tok1, "endcidrange")) {
199
21
    break;
200
21
  }
201
3.10k
  if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
202
3.08k
      !strcmp(tok2, "endcidrange") ||
203
3.07k
      !pst->getToken(tok3, sizeof(tok3), &n3) ||
204
3.05k
      !strcmp(tok3, "endcidrange")) {
205
56
    error(errSyntaxError, -1, "Illegal entry in cidrange block in CMap");
206
56
    break;
207
56
  }
208
3.04k
  if (tok1[0] == '<' && tok2[0] == '<' &&
209
1.59k
      n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
210
1.41k
    tok1[n1 - 1] = tok2[n1 - 1] = '\0';
211
1.41k
    sscanf(tok1 + 1, "%x", &start);
212
1.41k
    sscanf(tok2 + 1, "%x", &end);
213
1.41k
    n1 = (n1 - 2) / 2;
214
1.41k
    addCIDs(start, end, n1, (CID)atoi(tok3));
215
1.41k
  }
216
3.04k
      }
217
93
      pst->getToken(tok1, sizeof(tok1), &n1);
218
377k
    } else {
219
377k
      strcpy(tok1, tok2);
220
377k
    }
221
377k
  }
222
5.39k
  delete pst;
223
5.39k
}
224
225
5.39k
CMap::CMap(GString *collectionA, GString *cMapNameA) {
226
5.39k
  int i;
227
228
5.39k
  collection = collectionA;
229
5.39k
  cMapName = cMapNameA;
230
5.39k
  isIdent = gFalse;
231
5.39k
  wMode = 0;
232
5.39k
  vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
233
1.38M
  for (i = 0; i < 256; ++i) {
234
1.38M
    vector[i].isVector = gFalse;
235
1.38M
    vector[i].cid = 0;
236
1.38M
  }
237
5.39k
  refCnt = 1;
238
5.39k
}
239
240
1.30k
CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
241
1.30k
  collection = collectionA;
242
1.30k
  cMapName = cMapNameA;
243
1.30k
  isIdent = gTrue;
244
1.30k
  wMode = wModeA;
245
1.30k
  vector = NULL;
246
1.30k
  refCnt = 1;
247
1.30k
}
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.18k
void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
322
5.18k
  CMapVectorEntry *vec;
323
5.18k
  int byte, byte0, byte1;
324
5.18k
  Guint start1, end1, i, j, k;
325
326
5.18k
  start1 = start & 0xffffff00;
327
5.18k
  end1 = end & 0xffffff00;
328
11.4k
  for (i = start1; i <= end1; i += 0x100) {
329
6.30k
    vec = vector;
330
13.1k
    for (j = nBytes - 1; j >= 1; --j) {
331
6.81k
      byte = (i >> (8 * j)) & 0xff;
332
6.81k
      if (!vec[byte].isVector) {
333
1.86k
  vec[byte].isVector = gTrue;
334
1.86k
  vec[byte].vector =
335
1.86k
      (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
336
478k
  for (k = 0; k < 256; ++k) {
337
476k
    vec[byte].vector[k].isVector = gFalse;
338
476k
    vec[byte].vector[k].cid = 0;
339
476k
  }
340
1.86k
      }
341
6.81k
      vec = vec[byte].vector;
342
6.81k
    }
343
6.30k
    byte0 = (i < start) ? (start & 0xff) : 0;
344
6.30k
    byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff;
345
315k
    for (byte = byte0; byte <= byte1; ++byte) {
346
309k
      if (vec[byte].isVector) {
347
557
  error(errSyntaxError, -1, "Invalid CID ({0:x} [{1:d} bytes]) in CMap",
348
557
        i, nBytes);
349
308k
      } else {
350
308k
  vec[byte].cid = firstCID + ((i + byte) - start);
351
308k
      }
352
309k
    }
353
6.30k
  }
354
5.18k
}
355
356
6.70k
CMap::~CMap() {
357
6.70k
  delete collection;
358
6.70k
  if (cMapName) {
359
1.30k
    delete cMapName;
360
1.30k
  }
361
6.70k
  if (vector) {
362
5.39k
    freeCMapVector(vector);
363
5.39k
  }
364
6.70k
}
365
366
7.26k
void CMap::freeCMapVector(CMapVectorEntry *vec) {
367
7.26k
  int i;
368
369
1.86M
  for (i = 0; i < 256; ++i) {
370
1.85M
    if (vec[i].isVector) {
371
1.86k
      freeCMapVector(vec[i].vector);
372
1.86k
    }
373
1.85M
  }
374
7.26k
  gfree(vec);
375
7.26k
}
376
377
5.19k
void CMap::incRefCnt() {
378
5.19k
#if MULTITHREADED
379
5.19k
  gAtomicIncrement(&refCnt);
380
#else
381
  ++refCnt;
382
#endif
383
5.19k
}
384
385
11.8k
void CMap::decRefCnt() {
386
11.8k
  GBool done;
387
388
11.8k
#if MULTITHREADED
389
11.8k
  done = gAtomicDecrement(&refCnt) == 0;
390
#else
391
  done = --refCnt == 0;
392
#endif
393
11.8k
  if (done) {
394
6.70k
    delete this;
395
6.70k
  }
396
11.8k
}
397
398
3.90k
GBool CMap::match(GString *collectionA, GString *cMapNameA) {
399
3.90k
  return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
400
3.90k
}
401
402
153k
CID CMap::getCID(char *s, int len, CharCode *c, int *nUsed) {
403
153k
  CMapVectorEntry *vec;
404
153k
  CharCode cc;
405
153k
  int n, i;
406
407
153k
  vec = vector;
408
153k
  cc = 0;
409
153k
  n = 0;
410
153k
  while (vec && n < len) {
411
25.1k
    i = s[n++] & 0xff;
412
25.1k
    cc = (cc << 8) | i;
413
25.1k
    if (!vec[i].isVector) {
414
24.6k
      *c = cc;
415
24.6k
      *nUsed = n;
416
24.6k
      return vec[i].cid;
417
24.6k
    }
418
453
    vec = vec[i].vector;
419
453
  }
420
128k
  if (isIdent && len >= 2) {
421
    // identity CMap
422
123k
    *nUsed = 2;
423
123k
    *c = cc = ((s[0] & 0xff) << 8) + (s[1] & 0xff);
424
123k
    return cc;
425
123k
  }
426
5.53k
  *nUsed = 1;
427
5.53k
  *c = s[0] & 0xff;
428
5.53k
  return 0;
429
128k
}
430
431
//------------------------------------------------------------------------
432
433
37.8k
CMapCache::CMapCache() {
434
37.8k
  int i;
435
436
189k
  for (i = 0; i < cMapCacheSize; ++i) {
437
151k
    cache[i] = NULL;
438
151k
  }
439
37.8k
}
440
441
37.8k
CMapCache::~CMapCache() {
442
37.8k
  int i;
443
444
189k
  for (i = 0; i < cMapCacheSize; ++i) {
445
151k
    if (cache[i]) {
446
1.30k
      cache[i]->decRefCnt();
447
1.30k
    }
448
151k
  }
449
37.8k
}
450
451
CMap *CMapCache::getCMap(GString *collection, GString *cMapName,
452
5.64k
       GHash *usedCMaps) {
453
5.64k
  CMap *cmap;
454
5.64k
  int i, j;
455
456
5.64k
  if (cache[0] && cache[0]->match(collection, cMapName)) {
457
3.88k
    cache[0]->incRefCnt();
458
3.88k
    return cache[0];
459
3.88k
  }
460
7.01k
  for (i = 1; i < cMapCacheSize; ++i) {
461
5.26k
    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
5.26k
  }
471
1.75k
  if ((cmap = CMap::parse(this, collection, cMapName, usedCMaps))) {
472
1.30k
    if (cache[cMapCacheSize - 1]) {
473
0
      cache[cMapCacheSize - 1]->decRefCnt();
474
0
    }
475
5.22k
    for (j = cMapCacheSize - 1; j >= 1; --j) {
476
3.92k
      cache[j] = cache[j - 1];
477
3.92k
    }
478
1.30k
    cache[0] = cmap;
479
1.30k
    cmap->incRefCnt();
480
1.30k
    return cmap;
481
1.30k
  }
482
447
  return NULL;
483
1.75k
}