1 //========================================================================
5 // Copyright 2001-2003 Glyph & Cog, LLC
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 //========================================================================
27 #ifdef USE_GCC_PRAGMAS
28 #pragma implementation
36 #include "goo/gfile.h"
37 #include "goo/GooString.h"
39 #include "GlobalParams.h"
40 #include "PSTokenizer.h"
44 //------------------------------------------------------------------------
46 struct CMapVectorEntry
{
49 CMapVectorEntry
*vector
;
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
) {
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
);
78 } else if (obj
->isStream()) {
79 if (!(cMap
= CMap::parse(NULL
, collectionA
, obj
->getStream()))) {
80 error(errSyntaxError
, -1, "Invalid CMap in Type 0 font");
83 error(errSyntaxError
, -1, "Invalid Encoding in Type 0 font");
89 CMap
*CMap::parse(CMapCache
*cache
, GooString
*collectionA
,
90 GooString
*cMapNameA
) {
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
);
110 cMap
= new CMap(collectionA
->copy(), cMapNameA
->copy());
111 cMap
->parse2(cache
, &getCharFromFile
, f
);
118 CMap
*CMap::parse(CMapCache
*cache
, GooString
*collectionA
, Stream
*str
) {
122 cMap
= new CMap(collectionA
->copy(), NULL
);
124 if (!str
->getDict()->lookup("UseCMap", &obj1
)->isNull()) {
125 cMap
->useCMap(cache
, &obj1
);
130 cMap
->parse2(cache
, &getCharFromStream
, str
);
135 CMap
*CMap::parse(CMapCache
*cache
, GooString
*collectionA
,
136 GooString
*cMapNameA
, Stream
*stream
) {
140 char tok1
[256], tok2
[256], tok3
[256];
142 Guint start
, end
, code
;
146 pst
= new PSTokenizer(&getCharFromStream
, stream
);
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
);
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")) {
182 if (!pst
->getToken(tok2
, sizeof(tok2
), &n2
) ||
183 !strcmp(tok2
, "endcidchar")) {
184 error(errSyntaxError
, -1, "Illegal entry in cidchar block in CMap");
187 if (!(tok1
[0] == '<' && tok1
[n1
- 1] == '>' &&
188 n1
>= 4 && (n1
& 1) == 0)) {
189 error(errSyntaxError
, -1, "Illegal entry in cidchar block in CMap");
193 if (sscanf(tok1
+ 1, "%x", &code
) != 1) {
194 error(errSyntaxError
, -1, "Illegal entry in cidchar block in CMap");
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")) {
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");
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
);
219 cmap
->addCIDs(start
, end
, n1
, (CID
)atoi(tok3
));
222 pst
->getToken(tok1
, sizeof(tok1
), &n1
);
236 void CMap::parse2(CMapCache
*cache
, int (*getCharFunc
)(void *), void *data
) {
238 char tok1
[256], tok2
[256], tok3
[256];
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")) {
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")) {
258 if (!pst
->getToken(tok2
, sizeof(tok2
), &n2
) ||
259 !strcmp(tok2
, "endcidchar")) {
260 error(errSyntaxError
, -1, "Illegal entry in cidchar block in CMap");
263 if (!(tok1
[0] == '<' && tok1
[n1
- 1] == '>' &&
264 n1
>= 4 && (n1
& 1) == 0)) {
265 error(errSyntaxError
, -1, "Illegal entry in cidchar block in CMap");
269 if (sscanf(tok1
+ 1, "%x", &code
) != 1) {
270 error(errSyntaxError
, -1, "Illegal entry in cidchar block in CMap");
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")) {
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");
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
);
295 addCIDs(start
, end
, n1
, (CID
)atoi(tok3
));
298 pst
->getToken(tok1
, sizeof(tok1
), &n1
);
306 CMap::CMap(GooString
*collectionA
, GooString
*cMapNameA
) {
309 collection
= collectionA
;
310 cMapName
= cMapNameA
;
313 vector
= (CMapVectorEntry
*)gmallocn(256, sizeof(CMapVectorEntry
));
314 for (i
= 0; i
< 256; ++i
) {
315 vector
[i
].isVector
= gFalse
;
324 CMap::CMap(GooString
*collectionA
, GooString
*cMapNameA
, int wModeA
) {
325 collection
= collectionA
;
326 cMapName
= cMapNameA
;
336 void CMap::useCMap(CMapCache
*cache
, char *useName
) {
337 GooString
*useNameStr
;
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
346 subCMap
= cache
->getCMap(collection
, useNameStr
, NULL
);
348 subCMap
= globalParams
->getCMap(collection
, useNameStr
);
354 isIdent
= subCMap
->isIdent
;
355 if (subCMap
->vector
) {
356 copyVector(vector
, subCMap
->vector
);
358 subCMap
->decRefCnt();
361 void CMap::useCMap(CMapCache
*cache
, Object
*obj
) {
364 subCMap
= CMap::parse(cache
, collection
, obj
);
368 isIdent
= subCMap
->isIdent
;
369 if (subCMap
->vector
) {
370 copyVector(vector
, subCMap
->vector
);
372 subCMap
->decRefCnt();
375 void CMap::copyVector(CMapVectorEntry
*dest
, CMapVectorEntry
*src
) {
378 for (i
= 0; i
< 256; ++i
) {
379 if (src
[i
].isVector
) {
380 if (!dest
[i
].isVector
) {
381 dest
[i
].isVector
= gTrue
;
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
);
391 if (dest
[i
].isVector
) {
392 error(errSyntaxError
, -1, "Collision in usecmap");
394 dest
[i
].cid
= src
[i
].cid
;
400 void CMap::addCIDs(Guint start
, Guint end
, Guint nBytes
, CID firstCID
) {
401 CMapVectorEntry
*vec
;
407 for (i
= nBytes
- 1; i
>= 1; --i
) {
408 byte
= (start
>> (8 * i
)) & 0xff;
409 if (!vec
[byte
].isVector
) {
410 vec
[byte
].isVector
= gTrue
;
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
;
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",
437 freeCMapVector(vector
);
440 gDestroyMutex(&mutex
);
444 void CMap::freeCMapVector(CMapVectorEntry
*vec
) {
447 for (i
= 0; i
< 256; ++i
) {
448 if (vec
[i
].isVector
) {
449 freeCMapVector(vec
[i
].vector
);
455 void CMap::incRefCnt() {
461 gUnlockMutex(&mutex
);
465 void CMap::decRefCnt() {
471 done
= --refCnt
== 0;
473 gUnlockMutex(&mutex
);
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
;
492 while (vec
&& n
< len
) {
495 if (!vec
[i
].isVector
) {
502 if (isIdent
&& len
>= 2) {
505 *c
= cc
= ((s
[0] & 0xff) << 8) + (s
[1] & 0xff);
513 void CMap::setReverseMapVector(Guint startCode
, CMapVectorEntry
*vec
,
514 Guint
*rmap
, Guint rmapSize
, Guint ncand
) {
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
);
523 Guint cid
= vec
[i
].cid
;
525 if (cid
< rmapSize
) {
528 for (cand
= 0;cand
< ncand
;cand
++) {
529 Guint code
= startCode
+i
;
530 Guint idx
= cid
*ncand
+cand
;
531 if (rmap
[idx
] == 0) {
534 } else if (rmap
[idx
] == code
) {
543 void CMap::setReverseMap(Guint
*rmap
, Guint rmapSize
, Guint ncand
) {
544 setReverseMapVector(0,vector
,rmap
,rmapSize
,ncand
);
547 //------------------------------------------------------------------------
549 CMapCache::CMapCache() {
552 for (i
= 0; i
< cMapCacheSize
; ++i
) {
557 CMapCache::~CMapCache() {
560 for (i
= 0; i
< cMapCacheSize
; ++i
) {
562 cache
[i
]->decRefCnt();
567 CMap
*CMapCache::getCMap(GooString
*collection
, GooString
*cMapName
, Stream
*stream
) {
571 if (cache
[0] && cache
[0]->match(collection
, cMapName
)) {
572 cache
[0]->incRefCnt();
575 for (i
= 1; i
< cMapCacheSize
; ++i
) {
576 if (cache
[i
] && cache
[i
]->match(collection
, cMapName
)) {
578 for (j
= i
; j
>= 1; --j
) {
579 cache
[j
] = cache
[j
- 1];
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];