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