Coverage Report

Created: 2026-09-03 07:21

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