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