beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / poppler / CMap.cc
blob6731ab5a1fa87f6328f38ce4d0cea86bb818e338
1 //========================================================================
2 //
3 // CMap.cc
4 //
5 // Copyright 2001-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
16 // Copyright (C) 2008 Koji Otani <sho@bbr.jp>
17 // Copyright (C) 2008, 2009 Albert Astals Cid <aacid@kde.org>
18 // Copyright (C) 2013 Fabio D'Urso <fabiodurso@hotmail.it>
20 // To see a description of the changes please see the Changelog file that
21 // came with your tarball or type make ChangeLog if you are building from git
23 //========================================================================
25 #include <config.h>
27 #ifdef USE_GCC_PRAGMAS
28 #pragma implementation
29 #endif
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include "goo/gmem.h"
36 #include "goo/gfile.h"
37 #include "goo/GooString.h"
38 #include "Error.h"
39 #include "GlobalParams.h"
40 #include "PSTokenizer.h"
41 #include "CMap.h"
42 #include "Object.h"
44 //------------------------------------------------------------------------
46 struct CMapVectorEntry {
47 GBool isVector;
48 union {
49 CMapVectorEntry *vector;
50 CID cid;
54 //------------------------------------------------------------------------
56 static int getCharFromFile(void *data) {
57 return fgetc((FILE *)data);
60 static int getCharFromStream(void *data) {
61 return ((Stream *)data)->getChar();
64 //------------------------------------------------------------------------
66 CMap *CMap::parse(CMapCache *cache, GooString *collectionA, Object *obj) {
67 CMap *cMap;
68 GooString *cMapNameA;
70 if (obj->isName()) {
71 cMapNameA = new GooString(obj->getName());
72 if (!(cMap = globalParams->getCMap(collectionA, cMapNameA))) {
73 error(errSyntaxError, -1,
74 "Unknown CMap '{0:t}' for character collection '{1:t}'",
75 cMapNameA, collectionA);
77 delete cMapNameA;
78 } else if (obj->isStream()) {
79 if (!(cMap = CMap::parse(NULL, collectionA, obj->getStream()))) {
80 error(errSyntaxError, -1, "Invalid CMap in Type 0 font");
82 } else {
83 error(errSyntaxError, -1, "Invalid Encoding in Type 0 font");
84 return NULL;
86 return cMap;
89 CMap *CMap::parse(CMapCache *cache, GooString *collectionA,
90 GooString *cMapNameA) {
91 FILE *f;
92 CMap *cMap;
94 if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
96 // Check for an identity CMap.
97 if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
98 return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
100 if (!cMapNameA->cmp("Identity-V")) {
101 return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
104 error(errSyntaxError, -1,
105 "Couldn't find '{0:t}' CMap file for '{1:t}' collection",
106 cMapNameA, collectionA);
107 return NULL;
110 cMap = new CMap(collectionA->copy(), cMapNameA->copy());
111 cMap->parse2(cache, &getCharFromFile, f);
113 fclose(f);
115 return cMap;
118 CMap *CMap::parse(CMapCache *cache, GooString *collectionA, Stream *str) {
119 Object obj1;
120 CMap *cMap;
122 cMap = new CMap(collectionA->copy(), NULL);
124 if (!str->getDict()->lookup("UseCMap", &obj1)->isNull()) {
125 cMap->useCMap(cache, &obj1);
127 obj1.free();
129 str->reset();
130 cMap->parse2(cache, &getCharFromStream, str);
131 str->close();
132 return cMap;
135 CMap *CMap::parse(CMapCache *cache, GooString *collectionA,
136 GooString *cMapNameA, Stream *stream) {
137 FILE *f = NULL;
138 CMap *cmap;
139 PSTokenizer *pst;
140 char tok1[256], tok2[256], tok3[256];
141 int n1, n2, n3;
142 Guint start, end, code;
144 if (stream) {
145 stream->reset();
146 pst = new PSTokenizer(&getCharFromStream, stream);
147 } else {
148 if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
150 // Check for an identity CMap.
151 if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
152 return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
154 if (!cMapNameA->cmp("Identity-V")) {
155 return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
158 error(errSyntaxError, -1, "Couldn't find '{0:t}' CMap file for '{1:t}' collection",
159 cMapNameA, collectionA);
160 return NULL;
162 pst = new PSTokenizer(&getCharFromFile, f);
165 cmap = new CMap(collectionA->copy(), cMapNameA->copy());
167 pst->getToken(tok1, sizeof(tok1), &n1);
168 while (pst->getToken(tok2, sizeof(tok2), &n2)) {
169 if (!strcmp(tok2, "usecmap")) {
170 if (tok1[0] == '/') {
171 cmap->useCMap(cache, tok1 + 1);
173 pst->getToken(tok1, sizeof(tok1), &n1);
174 } else if (!strcmp(tok1, "/WMode")) {
175 cmap->wMode = atoi(tok2);
176 pst->getToken(tok1, sizeof(tok1), &n1);
177 } else if (!strcmp(tok2, "begincidchar")) {
178 while (pst->getToken(tok1, sizeof(tok1), &n1)) {
179 if (!strcmp(tok1, "endcidchar")) {
180 break;
182 if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
183 !strcmp(tok2, "endcidchar")) {
184 error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
185 break;
187 if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
188 n1 >= 4 && (n1 & 1) == 0)) {
189 error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
190 continue;
192 tok1[n1 - 1] = '\0';
193 if (sscanf(tok1 + 1, "%x", &code) != 1) {
194 error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
195 continue;
197 n1 = (n1 - 2) / 2;
198 cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
200 pst->getToken(tok1, sizeof(tok1), &n1);
201 } else if (!strcmp(tok2, "begincidrange")) {
202 while (pst->getToken(tok1, sizeof(tok1), &n1)) {
203 if (!strcmp(tok1, "endcidrange")) {
204 break;
206 if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
207 !strcmp(tok2, "endcidrange") ||
208 !pst->getToken(tok3, sizeof(tok3), &n3) ||
209 !strcmp(tok3, "endcidrange")) {
210 error(errSyntaxError, -1, "Illegal entry in cidrange block in CMap");
211 break;
213 if (tok1[0] == '<' && tok2[0] == '<' &&
214 n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
215 tok1[n1 - 1] = tok2[n1 - 1] = '\0';
216 sscanf(tok1 + 1, "%x", &start);
217 sscanf(tok2 + 1, "%x", &end);
218 n1 = (n1 - 2) / 2;
219 cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
222 pst->getToken(tok1, sizeof(tok1), &n1);
223 } else {
224 strcpy(tok1, tok2);
227 delete pst;
229 if (f) {
230 fclose(f);
233 return cmap;
236 void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data) {
237 PSTokenizer *pst;
238 char tok1[256], tok2[256], tok3[256];
239 int n1, n2, n3;
240 Guint start, end, code;
242 pst = new PSTokenizer(getCharFunc, data);
243 pst->getToken(tok1, sizeof(tok1), &n1);
244 while (pst->getToken(tok2, sizeof(tok2), &n2)) {
245 if (!strcmp(tok2, "usecmap")) {
246 if (tok1[0] == '/') {
247 useCMap(cache, tok1 + 1);
249 pst->getToken(tok1, sizeof(tok1), &n1);
250 } else if (!strcmp(tok1, "/WMode")) {
251 wMode = atoi(tok2);
252 pst->getToken(tok1, sizeof(tok1), &n1);
253 } else if (!strcmp(tok2, "begincidchar")) {
254 while (pst->getToken(tok1, sizeof(tok1), &n1)) {
255 if (!strcmp(tok1, "endcidchar")) {
256 break;
258 if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
259 !strcmp(tok2, "endcidchar")) {
260 error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
261 break;
263 if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
264 n1 >= 4 && (n1 & 1) == 0)) {
265 error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
266 continue;
268 tok1[n1 - 1] = '\0';
269 if (sscanf(tok1 + 1, "%x", &code) != 1) {
270 error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
271 continue;
273 n1 = (n1 - 2) / 2;
274 addCIDs(code, code, n1, (CID)atoi(tok2));
276 pst->getToken(tok1, sizeof(tok1), &n1);
277 } else if (!strcmp(tok2, "begincidrange")) {
278 while (pst->getToken(tok1, sizeof(tok1), &n1)) {
279 if (!strcmp(tok1, "endcidrange")) {
280 break;
282 if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
283 !strcmp(tok2, "endcidrange") ||
284 !pst->getToken(tok3, sizeof(tok3), &n3) ||
285 !strcmp(tok3, "endcidrange")) {
286 error(errSyntaxError, -1, "Illegal entry in cidrange block in CMap");
287 break;
289 if (tok1[0] == '<' && tok2[0] == '<' &&
290 n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
291 tok1[n1 - 1] = tok2[n1 - 1] = '\0';
292 sscanf(tok1 + 1, "%x", &start);
293 sscanf(tok2 + 1, "%x", &end);
294 n1 = (n1 - 2) / 2;
295 addCIDs(start, end, n1, (CID)atoi(tok3));
298 pst->getToken(tok1, sizeof(tok1), &n1);
299 } else {
300 strcpy(tok1, tok2);
303 delete pst;
306 CMap::CMap(GooString *collectionA, GooString *cMapNameA) {
307 int i;
309 collection = collectionA;
310 cMapName = cMapNameA;
311 isIdent = gFalse;
312 wMode = 0;
313 vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
314 for (i = 0; i < 256; ++i) {
315 vector[i].isVector = gFalse;
316 vector[i].cid = 0;
318 refCnt = 1;
319 #if MULTITHREADED
320 gInitMutex(&mutex);
321 #endif
324 CMap::CMap(GooString *collectionA, GooString *cMapNameA, int wModeA) {
325 collection = collectionA;
326 cMapName = cMapNameA;
327 isIdent = gTrue;
328 wMode = wModeA;
329 vector = NULL;
330 refCnt = 1;
331 #if MULTITHREADED
332 gInitMutex(&mutex);
333 #endif
336 void CMap::useCMap(CMapCache *cache, char *useName) {
337 GooString *useNameStr;
338 CMap *subCMap;
340 useNameStr = new GooString(useName);
341 // if cache is non-NULL, we already have a lock, and we can use
342 // CMapCache::getCMap() directly; otherwise, we need to use
343 // GlobalParams::getCMap() in order to acqure the lock need to use
344 // GlobalParams::getCMap
345 if (cache) {
346 subCMap = cache->getCMap(collection, useNameStr, NULL);
347 } else {
348 subCMap = globalParams->getCMap(collection, useNameStr);
350 delete useNameStr;
351 if (!subCMap) {
352 return;
354 isIdent = subCMap->isIdent;
355 if (subCMap->vector) {
356 copyVector(vector, subCMap->vector);
358 subCMap->decRefCnt();
361 void CMap::useCMap(CMapCache *cache, Object *obj) {
362 CMap *subCMap;
364 subCMap = CMap::parse(cache, collection, obj);
365 if (!subCMap) {
366 return;
368 isIdent = subCMap->isIdent;
369 if (subCMap->vector) {
370 copyVector(vector, subCMap->vector);
372 subCMap->decRefCnt();
375 void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
376 int i, j;
378 for (i = 0; i < 256; ++i) {
379 if (src[i].isVector) {
380 if (!dest[i].isVector) {
381 dest[i].isVector = gTrue;
382 dest[i].vector =
383 (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
384 for (j = 0; j < 256; ++j) {
385 dest[i].vector[j].isVector = gFalse;
386 dest[i].vector[j].cid = 0;
389 copyVector(dest[i].vector, src[i].vector);
390 } else {
391 if (dest[i].isVector) {
392 error(errSyntaxError, -1, "Collision in usecmap");
393 } else {
394 dest[i].cid = src[i].cid;
400 void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
401 CMapVectorEntry *vec;
402 CID cid;
403 int byte;
404 Guint i, j;
406 vec = vector;
407 for (i = nBytes - 1; i >= 1; --i) {
408 byte = (start >> (8 * i)) & 0xff;
409 if (!vec[byte].isVector) {
410 vec[byte].isVector = gTrue;
411 vec[byte].vector =
412 (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
413 for (j = 0; j < 256; ++j) {
414 vec[byte].vector[j].isVector = gFalse;
415 vec[byte].vector[j].cid = 0;
418 vec = vec[byte].vector;
420 cid = firstCID;
421 for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
422 if (vec[byte].isVector) {
423 error(errSyntaxError, -1,
424 "Invalid CID ({0:ux} - {1:ux} [{2:ud} bytes]) in CMap",
425 start, end, nBytes);
426 } else {
427 vec[byte].cid = cid;
429 ++cid;
433 CMap::~CMap() {
434 delete collection;
435 delete cMapName;
436 if (vector) {
437 freeCMapVector(vector);
439 #if MULTITHREADED
440 gDestroyMutex(&mutex);
441 #endif
444 void CMap::freeCMapVector(CMapVectorEntry *vec) {
445 int i;
447 for (i = 0; i < 256; ++i) {
448 if (vec[i].isVector) {
449 freeCMapVector(vec[i].vector);
452 gfree(vec);
455 void CMap::incRefCnt() {
456 #if MULTITHREADED
457 gLockMutex(&mutex);
458 #endif
459 ++refCnt;
460 #if MULTITHREADED
461 gUnlockMutex(&mutex);
462 #endif
465 void CMap::decRefCnt() {
466 GBool done;
468 #if MULTITHREADED
469 gLockMutex(&mutex);
470 #endif
471 done = --refCnt == 0;
472 #if MULTITHREADED
473 gUnlockMutex(&mutex);
474 #endif
475 if (done) {
476 delete this;
480 GBool CMap::match(GooString *collectionA, GooString *cMapNameA) {
481 return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
484 CID CMap::getCID(char *s, int len, CharCode *c, int *nUsed) {
485 CMapVectorEntry *vec;
486 CharCode cc;
487 int n, i;
489 vec = vector;
490 cc = 0;
491 n = 0;
492 while (vec && n < len) {
493 i = s[n++] & 0xff;
494 cc = (cc << 8) | i;
495 if (!vec[i].isVector) {
496 *c = cc;
497 *nUsed = n;
498 return vec[i].cid;
500 vec = vec[i].vector;
502 if (isIdent && len >= 2) {
503 // identity CMap
504 *nUsed = 2;
505 *c = cc = ((s[0] & 0xff) << 8) + (s[1] & 0xff);
506 return cc;
508 *nUsed = 1;
509 *c = s[0] & 0xff;
510 return 0;
513 void CMap::setReverseMapVector(Guint startCode, CMapVectorEntry *vec,
514 Guint *rmap, Guint rmapSize, Guint ncand) {
515 int i;
517 if (vec == 0) return;
518 for (i = 0;i < 256;i++) {
519 if (vec[i].isVector) {
520 setReverseMapVector((startCode+i) << 8,
521 vec[i].vector,rmap,rmapSize,ncand);
522 } else {
523 Guint cid = vec[i].cid;
525 if (cid < rmapSize) {
526 Guint cand;
528 for (cand = 0;cand < ncand;cand++) {
529 Guint code = startCode+i;
530 Guint idx = cid*ncand+cand;
531 if (rmap[idx] == 0) {
532 rmap[idx] = code;
533 break;
534 } else if (rmap[idx] == code) {
535 break;
543 void CMap::setReverseMap(Guint *rmap, Guint rmapSize, Guint ncand) {
544 setReverseMapVector(0,vector,rmap,rmapSize,ncand);
547 //------------------------------------------------------------------------
549 CMapCache::CMapCache() {
550 int i;
552 for (i = 0; i < cMapCacheSize; ++i) {
553 cache[i] = NULL;
557 CMapCache::~CMapCache() {
558 int i;
560 for (i = 0; i < cMapCacheSize; ++i) {
561 if (cache[i]) {
562 cache[i]->decRefCnt();
567 CMap *CMapCache::getCMap(GooString *collection, GooString *cMapName, Stream *stream) {
568 CMap *cmap;
569 int i, j;
571 if (cache[0] && cache[0]->match(collection, cMapName)) {
572 cache[0]->incRefCnt();
573 return cache[0];
575 for (i = 1; i < cMapCacheSize; ++i) {
576 if (cache[i] && cache[i]->match(collection, cMapName)) {
577 cmap = cache[i];
578 for (j = i; j >= 1; --j) {
579 cache[j] = cache[j - 1];
581 cache[0] = cmap;
582 cmap->incRefCnt();
583 return cmap;
586 if ((cmap = CMap::parse(this, collection, cMapName, stream))) {
587 if (cache[cMapCacheSize - 1]) {
588 cache[cMapCacheSize - 1]->decRefCnt();
590 for (j = cMapCacheSize - 1; j >= 1; --j) {
591 cache[j] = cache[j - 1];
593 cache[0] = cmap;
594 cmap->incRefCnt();
595 return cmap;
597 return NULL;