Coverage Report

Created: 2025-08-29 06:21

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