25f3af75e7066d81c3a5dc7ffb22d1cc3860cbaf
[swftools.git] / pdf2swf / xpdf / CMap.cc
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 "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 static int getCharFromFile(void *data) {
40   return fgetc((FILE *)data);
41 }
42
43 //------------------------------------------------------------------------
44
45 CMap *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
135 CMap::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 #if MULTITHREADED
148   gInitMutex(&mutex);
149 #endif
150 }
151
152 CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
153   collection = collectionA;
154   cMapName = cMapNameA;
155   wMode = wModeA;
156   vector = NULL;
157   refCnt = 1;
158 #if MULTITHREADED
159   gInitMutex(&mutex);
160 #endif
161 }
162
163 void CMap::useCMap(CMapCache *cache, char *useName) {
164   GString *useNameStr;
165   CMap *subCMap;
166
167   useNameStr = new GString(useName);
168   subCMap = cache->getCMap(collection, useNameStr);
169   delete useNameStr;
170   if (!subCMap) {
171     return;
172   }
173   copyVector(vector, subCMap->vector);
174   subCMap->decRefCnt();
175 }
176
177 void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
178   int i, j;
179
180   for (i = 0; i < 256; ++i) {
181     if (src[i].isVector) {
182       if (!dest[i].isVector) {
183         dest[i].isVector = gTrue;
184         dest[i].vector =
185           (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
186         for (j = 0; j < 256; ++j) {
187           dest[i].vector[j].isVector = gFalse;
188           dest[i].vector[j].cid = 0;
189         }
190       }
191       copyVector(dest[i].vector, src[i].vector);
192     } else {
193       if (dest[i].isVector) {
194         error(-1, "Collision in usecmap");
195       } else {
196         dest[i].cid = src[i].cid;
197       }
198     }
199   }
200 }
201
202 void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
203                         Guint nBytes) {
204   Guint start2, end2;
205   int startByte, endByte, i, j;
206
207   if (nBytes > 1) {
208     startByte = (start >> (8 * (nBytes - 1))) & 0xff;
209     endByte = (end >> (8 * (nBytes - 1))) & 0xff;
210     start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
211     end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
212     for (i = startByte; i <= endByte; ++i) {
213       if (!vec[i].isVector) {
214         vec[i].isVector = gTrue;
215         vec[i].vector =
216           (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
217         for (j = 0; j < 256; ++j) {
218           vec[i].vector[j].isVector = gFalse;
219           vec[i].vector[j].cid = 0;
220         }
221       }
222       addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
223     }
224   }
225 }
226
227 void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
228   CMapVectorEntry *vec;
229   CID cid;
230   int byte;
231   Guint i;
232
233   vec = vector;
234   for (i = nBytes - 1; i >= 1; --i) {
235     byte = (start >> (8 * i)) & 0xff;
236     if (!vec[byte].isVector) {
237       error(-1, "Invalid CID (%*x - %*x) in CMap",
238             2*nBytes, start, 2*nBytes, end);
239       return;
240     }
241     vec = vec[byte].vector;
242   }
243   cid = firstCID;
244   for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
245     if (vec[byte].isVector) {
246       error(-1, "Invalid CID (%*x - %*x) in CMap",
247             2*nBytes, start, 2*nBytes, end);
248     } else {
249       vec[byte].cid = cid;
250     }
251     ++cid;
252   }
253 }
254
255 CMap::~CMap() {
256   delete collection;
257   delete cMapName;
258   if (vector) {
259     freeCMapVector(vector);
260   }
261 #if MULTITHREADED
262   gDestroyMutex(&mutex);
263 #endif
264 }
265
266 void CMap::freeCMapVector(CMapVectorEntry *vec) {
267   int i;
268
269   for (i = 0; i < 256; ++i) {
270     if (vec[i].isVector) {
271       freeCMapVector(vec[i].vector);
272     }
273   }
274   gfree(vec);
275 }
276
277 void CMap::incRefCnt() {
278 #if MULTITHREADED
279   gLockMutex(&mutex);
280 #endif
281   ++refCnt;
282 #if MULTITHREADED
283   gUnlockMutex(&mutex);
284 #endif
285 }
286
287 void CMap::decRefCnt() {
288   GBool done;
289
290 #if MULTITHREADED
291   gLockMutex(&mutex);
292 #endif
293   done = --refCnt == 0;
294 #if MULTITHREADED
295   gUnlockMutex(&mutex);
296 #endif
297   if (done) {
298     delete this;
299   }
300 }
301
302 GBool CMap::match(GString *collectionA, GString *cMapNameA) {
303   return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
304 }
305
306 CID CMap::getCID(char *s, int len, int *nUsed) {
307   CMapVectorEntry *vec;
308   int n, i;
309
310   if (!(vec = vector)) {
311     // identity CMap
312     *nUsed = 2;
313     if (len < 2) {
314       return 0;
315     }
316     return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
317   }
318   n = 0;
319   while (1) {
320     if (n >= len) {
321       *nUsed = n;
322       return 0;
323     }
324     i = s[n++] & 0xff;
325     if (!vec[i].isVector) {
326       *nUsed = n;
327       return vec[i].cid;
328     }
329     vec = vec[i].vector;
330   }
331 }
332
333 //------------------------------------------------------------------------
334
335 CMapCache::CMapCache() {
336   int i;
337
338   for (i = 0; i < cMapCacheSize; ++i) {
339     cache[i] = NULL;
340   }
341 }
342
343 CMapCache::~CMapCache() {
344   int i;
345
346   for (i = 0; i < cMapCacheSize; ++i) {
347     if (cache[i]) {
348       cache[i]->decRefCnt();
349     }
350   }
351 }
352
353 CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
354   CMap *cmap;
355   int i, j;
356
357   if (cache[0] && cache[0]->match(collection, cMapName)) {
358     cache[0]->incRefCnt();
359     return cache[0];
360   }
361   for (i = 1; i < cMapCacheSize; ++i) {
362     if (cache[i] && cache[i]->match(collection, cMapName)) {
363       cmap = cache[i];
364       for (j = i; j >= 1; --j) {
365         cache[j] = cache[j - 1];
366       }
367       cache[0] = cmap;
368       cmap->incRefCnt();
369       return cmap;
370     }
371   }
372   if ((cmap = CMap::parse(this, collection, cMapName))) {
373     if (cache[cMapCacheSize - 1]) {
374       cache[cMapCacheSize - 1]->decRefCnt();
375     }
376     for (j = cMapCacheSize - 1; j >= 1; --j) {
377       cache[j] = cache[j - 1];
378     }
379     cache[0] = cmap;
380     cmap->incRefCnt();
381     return cmap;
382   }
383   return NULL;
384 }