Coverage Report

Created: 2026-07-04 07:15

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