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