CMap.cpp
1//========================================================================
2//
3// CMap.cpp
4//
5// Copyright 2001-2002 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <aconf.h>
10
11#ifdef USE_GCC_PRAGMAS
12#pragma implementation
13#endif
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <ctype.h>
19#include "gmem.h"
20#include "gfile.h"
21#include "GString.h"
22#include "Error.h"
23#include "GlobalParams.h"
24#include "PSTokenizer.h"
25#include "CMap.h"
26
27//------------------------------------------------------------------------
28
29struct CMapVectorEntry {
30 GBool isVector;
31 union {
32 CMapVectorEntry *vector;
33 CID cid;
34 };
35};
36
37//------------------------------------------------------------------------
38
39static int getCharFromFile(void *data) {
40 return fgetc((FILE *)data);
41}
42
43//------------------------------------------------------------------------
44
45CMap *CMap::parse(CMapCache *cache, GString *collectionA,
46 GString *cMapNameA) {
47 FILE *f;
48 CMap *cmap;
49 PSTokenizer *pst;
50 char tok1[256], tok2[256], tok3[256];
51 int n1, n2, n3;
52 Guint start, end;
53
54 if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
55
56 // Check for an identity CMap.
57 if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
58 return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
59 }
60 if (!cMapNameA->cmp("Identity-V")) {
61 return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
62 }
63
64 error(-1, "Couldn't find '%s' CMap file for '%s' collection",
65 cMapNameA->getCString(), collectionA->getCString());
66 return NULL;
67 }
68
69 cmap = new CMap(collectionA->copy(), cMapNameA->copy());
70
71 pst = new PSTokenizer(&getCharFromFile, f);
72 pst->getToken(tok1, sizeof(tok1), &n1);
73 while (pst->getToken(tok2, sizeof(tok2), &n2)) {
74 if (!strcmp(tok2, "usecmap")) {
75 if (tok1[0] == '/') {
76 cmap->useCMap(cache, tok1 + 1);
77 }
78 pst->getToken(tok1, sizeof(tok1), &n1);
79 } else if (!strcmp(tok1, "/WMode")) {
80 cmap->wMode = atoi(tok2);
81 pst->getToken(tok1, sizeof(tok1), &n1);
82 } else if (!strcmp(tok2, "begincodespacerange")) {
83 while (pst->getToken(tok1, sizeof(tok1), &n1)) {
84 if (!strcmp(tok1, "endcodespacerange")) {
85 break;
86 }
87 if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
88 !strcmp(tok2, "endcodespacerange")) {
89 error(-1, "Illegal entry in codespacerange block in CMap");
90 break;
91 }
92 if (tok1[0] == '<' && tok2[0] == '<' &&
93 n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
94 tok1[n1 - 1] = tok2[n1 - 1] = '\0';
95 sscanf(tok1 + 1, "%x", &start);
96 sscanf(tok2 + 1, "%x", &end);
97 n1 = (n1 - 2) / 2;
98 cmap->addCodeSpace(cmap->vector, start, end, n1);
99 }
100 }
101 pst->getToken(tok1, sizeof(tok1), &n1);
102 } else if (!strcmp(tok2, "begincidrange")) {
103 while (pst->getToken(tok1, sizeof(tok1), &n1)) {
104 if (!strcmp(tok1, "endcidrange")) {
105 break;
106 }
107 if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
108 !strcmp(tok2, "endcidrange") ||
109 !pst->getToken(tok3, sizeof(tok3), &n3) ||
110 !strcmp(tok3, "endcidrange")) {
111 error(-1, "Illegal entry in cidrange block in CMap");
112 break;
113 }
114 if (tok1[0] == '<' && tok2[0] == '<' &&
115 n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
116 tok1[n1 - 1] = tok2[n1 - 1] = '\0';
117 sscanf(tok1 + 1, "%x", &start);
118 sscanf(tok2 + 1, "%x", &end);
119 n1 = (n1 - 2) / 2;
120 cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
121 }
122 }
123 pst->getToken(tok1, sizeof(tok1), &n1);
124 } else {
125 strcpy(tok1, tok2);
126 }
127 }
128 delete pst;
129
130 fclose(f);
131
132 return cmap;
133}
134
135CMap::CMap(GString *collectionA, GString *cMapNameA) {
136 int i;
137
138 collection = collectionA;
139 cMapName = cMapNameA;
140 wMode = 0;
141 vector = (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
142 for (i = 0; i < 256; ++i) {
143 vector[i].isVector = gFalse;
144 vector[i].cid = 0;
145 }
146 refCnt = 1;
147}
148
149CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
150 collection = collectionA;
151 cMapName = cMapNameA;
152 wMode = wModeA;
153 vector = NULL;
154 refCnt = 1;
155}
156
157void CMap::useCMap(CMapCache *cache, char *useName) {
158 GString *useNameStr;
159 CMap *subCMap;
160
161 useNameStr = new GString(useName);
162 subCMap = cache->getCMap(collection, useNameStr);
163 delete useNameStr;
164 if (!subCMap) {
165 return;
166 }
167 copyVector(vector, subCMap->vector);
168 subCMap->decRefCnt();
169}
170
171void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
172 int i, j;
173
174 for (i = 0; i < 256; ++i) {
175 if (src[i].isVector) {
176 if (!dest[i].isVector) {
177 dest[i].isVector = gTrue;
178 dest[i].vector =
179 (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
180 for (j = 0; j < 256; ++j) {
181 dest[i].vector[j].isVector = gFalse;
182 dest[i].vector[j].cid = 0;
183 }
184 }
185 copyVector(dest[i].vector, src[i].vector);
186 } else {
187 if (dest[i].isVector) {
188 error(-1, "Collision in usecmap");
189 } else {
190 dest[i].cid = src[i].cid;
191 }
192 }
193 }
194}
195
196void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
197 Guint nBytes) {
198 Guint start2, end2;
199 int startByte, endByte, i, j;
200
201 if (nBytes > 1) {
202 startByte = (start >> (8 * (nBytes - 1))) & 0xff;
203 endByte = (end >> (8 * (nBytes - 1))) & 0xff;
204 start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
205 end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
206 for (i = startByte; i <= endByte; ++i) {
207 if (!vec[i].isVector) {
208 vec[i].isVector = gTrue;
209 vec[i].vector =
210 (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
211 for (j = 0; j < 256; ++j) {
212 vec[i].vector[j].isVector = gFalse;
213 vec[i].vector[j].cid = 0;
214 }
215 }
216 addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
217 }
218 }
219}
220
221void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
222 CMapVectorEntry *vec;
223 CID cid;
224 int byte;
225 Guint i;
226
227 vec = vector;
228 for (i = nBytes - 1; i >= 1; --i) {
229 byte = (start >> (8 * i)) & 0xff;
230 if (!vec[byte].isVector) {
231 error(-1, "Invalid CID (%*x - %*x) in CMap",
232 2*nBytes, start, 2*nBytes, end);
233 return;
234 }
235 vec = vec[byte].vector;
236 }
237 cid = firstCID;
238 for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
239 if (vec[byte].isVector) {
240 error(-1, "Invalid CID (%*x - %*x) in CMap",
241 2*nBytes, start, 2*nBytes, end);
242 } else {
243 vec[byte].cid = cid;
244 }
245 ++cid;
246 }
247}
248
249CMap::~CMap() {
250 delete collection;
251 delete cMapName;
252 if (vector) {
253 freeCMapVector(vector);
254 }
255}
256
257void CMap::freeCMapVector(CMapVectorEntry *vec) {
258 int i;
259
260 for (i = 0; i < 256; ++i) {
261 if (vec[i].isVector) {
262 freeCMapVector(vec[i].vector);
263 }
264 }
265 gfree(vec);
266}
267
268void CMap::incRefCnt() {
269 ++refCnt;
270}
271
272void CMap::decRefCnt() {
273 if (--refCnt == 0) {
274 delete this;
275 }
276}
277
278GBool CMap::match(GString *collectionA, GString *cMapNameA) {
279 return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
280}
281
282CID CMap::getCID(char *s, int len, int *nUsed) {
283 CMapVectorEntry *vec;
284 int n, i;
285
286 if (!(vec = vector)) {
287 // identity CMap
288 *nUsed = 2;
289 if (len < 2) {
290 return 0;
291 }
292 return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
293 }
294 n = 0;
295 while (1) {
296 if (n >= len) {
297 *nUsed = n;
298 return 0;
299 }
300 i = s[n++] & 0xff;
301 if (!vec[i].isVector) {
302 *nUsed = n;
303 return vec[i].cid;
304 }
305 vec = vec[i].vector;
306 }
307}
308
309//------------------------------------------------------------------------
310
311CMapCache::CMapCache() {
312 int i;
313
314 for (i = 0; i < cMapCacheSize; ++i) {
315 cache[i] = NULL;
316 }
317}
318
319CMapCache::~CMapCache() {
320 int i;
321
322 for (i = 0; i < cMapCacheSize; ++i) {
323 if (cache[i]) {
324 cache[i]->decRefCnt();
325 }
326 }
327}
328
329CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
330 CMap *cmap;
331 int i, j;
332
333 if (cache[0] && cache[0]->match(collection, cMapName)) {
334 cache[0]->incRefCnt();
335 return cache[0];
336 }
337 for (i = 1; i < cMapCacheSize; ++i) {
338 if (cache[i] && cache[i]->match(collection, cMapName)) {
339 cmap = cache[i];
340 for (j = i; j >= 1; --j) {
341 cache[j] = cache[j - 1];
342 }
343 cache[0] = cmap;
344 cmap->incRefCnt();
345 return cmap;
346 }
347 }
348 if ((cmap = CMap::parse(this, collection, cMapName))) {
349 if (cache[cMapCacheSize - 1]) {
350 cache[cMapCacheSize - 1]->decRefCnt();
351 }
352 for (j = cMapCacheSize - 1; j >= 1; --j) {
353 cache[j] = cache[j - 1];
354 }
355 cache[0] = cmap;
356 cmap->incRefCnt();
357 return cmap;
358 }
359 return NULL;
360}