readded png save, added xpm support
[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, code;
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, "begincidchar")) {
103       while (pst->getToken(tok1, sizeof(tok1), &n1)) {
104         if (!strcmp(tok1, "endcidchar")) {
105           break;
106         }
107         if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
108             !strcmp(tok2, "endcidchar")) {
109           error(-1, "Illegal entry in cidchar block in CMap");
110           break;
111         }
112         if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
113               n1 >= 4 && (n1 & 1) == 0)) {
114           error(-1, "Illegal entry in cidchar block in CMap");
115           continue;
116         }
117         tok1[n1 - 1] = '\0';
118         if (sscanf(tok1 + 1, "%x", &code) != 1) {
119           error(-1, "Illegal entry in cidchar block in CMap");
120           continue;
121         }
122         n1 = (n1 - 2) / 2;
123         cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
124       }
125       pst->getToken(tok1, sizeof(tok1), &n1);
126     } else if (!strcmp(tok2, "begincidrange")) {
127       while (pst->getToken(tok1, sizeof(tok1), &n1)) {
128         if (!strcmp(tok1, "endcidrange")) {
129           break;
130         }
131         if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
132             !strcmp(tok2, "endcidrange") ||
133             !pst->getToken(tok3, sizeof(tok3), &n3) ||
134             !strcmp(tok3, "endcidrange")) {
135           error(-1, "Illegal entry in cidrange block in CMap");
136           break;
137         }
138         if (tok1[0] == '<' && tok2[0] == '<' &&
139             n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
140           tok1[n1 - 1] = tok2[n1 - 1] = '\0';
141           sscanf(tok1 + 1, "%x", &start);
142           sscanf(tok2 + 1, "%x", &end);
143           n1 = (n1 - 2) / 2;
144           cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
145         }
146       }
147       pst->getToken(tok1, sizeof(tok1), &n1);
148     } else {
149       strcpy(tok1, tok2);
150     }
151   }
152   delete pst;
153
154   fclose(f);
155
156   return cmap;
157 }
158
159 CMap::CMap(GString *collectionA, GString *cMapNameA) {
160   int i;
161
162   collection = collectionA;
163   cMapName = cMapNameA;
164   wMode = 0;
165   vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
166   for (i = 0; i < 256; ++i) {
167     vector[i].isVector = gFalse;
168     vector[i].cid = 0;
169   }
170   refCnt = 1;
171 #if MULTITHREADED
172   gInitMutex(&mutex);
173 #endif
174 }
175
176 CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
177   collection = collectionA;
178   cMapName = cMapNameA;
179   wMode = wModeA;
180   vector = NULL;
181   refCnt = 1;
182 #if MULTITHREADED
183   gInitMutex(&mutex);
184 #endif
185 }
186
187 void CMap::useCMap(CMapCache *cache, char *useName) {
188   GString *useNameStr;
189   CMap *subCMap;
190
191   useNameStr = new GString(useName);
192   subCMap = cache->getCMap(collection, useNameStr);
193   delete useNameStr;
194   if (!subCMap) {
195     return;
196   }
197   copyVector(vector, subCMap->vector);
198   subCMap->decRefCnt();
199 }
200
201 void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
202   int i, j;
203
204   for (i = 0; i < 256; ++i) {
205     if (src[i].isVector) {
206       if (!dest[i].isVector) {
207         dest[i].isVector = gTrue;
208         dest[i].vector =
209           (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
210         for (j = 0; j < 256; ++j) {
211           dest[i].vector[j].isVector = gFalse;
212           dest[i].vector[j].cid = 0;
213         }
214       }
215       copyVector(dest[i].vector, src[i].vector);
216     } else {
217       if (dest[i].isVector) {
218         error(-1, "Collision in usecmap");
219       } else {
220         dest[i].cid = src[i].cid;
221       }
222     }
223   }
224 }
225
226 void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
227                         Guint nBytes) {
228   Guint start2, end2;
229   int startByte, endByte, i, j;
230
231   if (nBytes > 1) {
232     startByte = (start >> (8 * (nBytes - 1))) & 0xff;
233     endByte = (end >> (8 * (nBytes - 1))) & 0xff;
234     start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
235     end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
236     for (i = startByte; i <= endByte; ++i) {
237       if (!vec[i].isVector) {
238         vec[i].isVector = gTrue;
239         vec[i].vector =
240           (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
241         for (j = 0; j < 256; ++j) {
242           vec[i].vector[j].isVector = gFalse;
243           vec[i].vector[j].cid = 0;
244         }
245       }
246       addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
247     }
248   }
249 }
250
251 void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
252   CMapVectorEntry *vec;
253   CID cid;
254   int byte;
255   Guint i;
256
257   vec = vector;
258   for (i = nBytes - 1; i >= 1; --i) {
259     byte = (start >> (8 * i)) & 0xff;
260     if (!vec[byte].isVector) {
261       error(-1, "Invalid CID (%0*x - %0*x) in CMap",
262             2*nBytes, start, 2*nBytes, end);
263       return;
264     }
265     vec = vec[byte].vector;
266   }
267   cid = firstCID;
268   for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
269     if (vec[byte].isVector) {
270       error(-1, "Invalid CID (%0*x - %0*x) in CMap",
271             2*nBytes, start, 2*nBytes, end);
272     } else {
273       vec[byte].cid = cid;
274     }
275     ++cid;
276   }
277 }
278
279 CMap::~CMap() {
280   delete collection;
281   delete cMapName;
282   if (vector) {
283     freeCMapVector(vector);
284   }
285 #if MULTITHREADED
286   gDestroyMutex(&mutex);
287 #endif
288 }
289
290 void CMap::freeCMapVector(CMapVectorEntry *vec) {
291   int i;
292
293   for (i = 0; i < 256; ++i) {
294     if (vec[i].isVector) {
295       freeCMapVector(vec[i].vector);
296     }
297   }
298   gfree(vec);
299 }
300
301 void CMap::incRefCnt() {
302 #if MULTITHREADED
303   gLockMutex(&mutex);
304 #endif
305   ++refCnt;
306 #if MULTITHREADED
307   gUnlockMutex(&mutex);
308 #endif
309 }
310
311 void CMap::decRefCnt() {
312   GBool done;
313
314 #if MULTITHREADED
315   gLockMutex(&mutex);
316 #endif
317   done = --refCnt == 0;
318 #if MULTITHREADED
319   gUnlockMutex(&mutex);
320 #endif
321   if (done) {
322     delete this;
323   }
324 }
325
326 GBool CMap::match(GString *collectionA, GString *cMapNameA) {
327   return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
328 }
329
330 CID CMap::getCID(char *s, int len, int *nUsed) {
331   CMapVectorEntry *vec;
332   int n, i;
333
334   if (!(vec = vector)) {
335     // identity CMap
336     *nUsed = 2;
337     if (len < 2) {
338       return 0;
339     }
340     return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
341   }
342   n = 0;
343   while (1) {
344     if (n >= len) {
345       *nUsed = n;
346       return 0;
347     }
348     i = s[n++] & 0xff;
349     if (!vec[i].isVector) {
350       *nUsed = n;
351       return vec[i].cid;
352     }
353     vec = vec[i].vector;
354   }
355 }
356
357 //------------------------------------------------------------------------
358
359 CMapCache::CMapCache() {
360   int i;
361
362   for (i = 0; i < cMapCacheSize; ++i) {
363     cache[i] = NULL;
364   }
365 }
366
367 CMapCache::~CMapCache() {
368   int i;
369
370   for (i = 0; i < cMapCacheSize; ++i) {
371     if (cache[i]) {
372       cache[i]->decRefCnt();
373     }
374   }
375 }
376
377 CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
378   CMap *cmap;
379   int i, j;
380
381   if (cache[0] && cache[0]->match(collection, cMapName)) {
382     cache[0]->incRefCnt();
383     return cache[0];
384   }
385   for (i = 1; i < cMapCacheSize; ++i) {
386     if (cache[i] && cache[i]->match(collection, cMapName)) {
387       cmap = cache[i];
388       for (j = i; j >= 1; --j) {
389         cache[j] = cache[j - 1];
390       }
391       cache[0] = cmap;
392       cmap->incRefCnt();
393       return cmap;
394     }
395   }
396   if ((cmap = CMap::parse(this, collection, cMapName))) {
397     if (cache[cMapCacheSize - 1]) {
398       cache[cMapCacheSize - 1]->decRefCnt();
399     }
400     for (j = cMapCacheSize - 1; j >= 1; --j) {
401       cache[j] = cache[j - 1];
402     }
403     cache[0] = cmap;
404     cmap->incRefCnt();
405     return cmap;
406   }
407   return NULL;
408 }