Coverage Report

Created: 2026-02-04 06:13

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