1 | //======================================================================== |
2 | // |
3 | // CMap.cc |
4 | // |
5 | // Copyright 2001-2003 Glyph & Cog, LLC |
6 | // |
7 | //======================================================================== |
8 | |
9 | //======================================================================== |
10 | // |
11 | // Modified under the Poppler project - http://poppler.freedesktop.org |
12 | // |
13 | // All changes made under the Poppler project to this file are licensed |
14 | // under GPL version 2 or later |
15 | // |
16 | // Copyright (C) 2008 Koji Otani <sho@bbr.jp> |
17 | // Copyright (C) 2008, 2009, 2017-2021 Albert Astals Cid <aacid@kde.org> |
18 | // Copyright (C) 2013 Fabio D'Urso <fabiodurso@hotmail.it> |
19 | // Copyright (C) 2017 Adrian Johnson <ajohnson@redneon.com> |
20 | // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
21 | // Copyright (C) 2019 LE GARREC Vincent <legarrec.vincent@gmail.com> |
22 | // |
23 | // To see a description of the changes please see the Changelog file that |
24 | // came with your tarball or type make ChangeLog if you are building from git |
25 | // |
26 | //======================================================================== |
27 | |
28 | #include <config.h> |
29 | |
30 | #include <cstdio> |
31 | #include <cstdlib> |
32 | #include <cstring> |
33 | #include <cctype> |
34 | #include "goo/gmem.h" |
35 | #include "goo/gfile.h" |
36 | #include "goo/GooString.h" |
37 | #include "Error.h" |
38 | #include "GlobalParams.h" |
39 | #include "PSTokenizer.h" |
40 | #include "CMap.h" |
41 | #include "Object.h" |
42 | |
43 | //------------------------------------------------------------------------ |
44 | |
45 | struct CMapVectorEntry |
46 | { |
47 | bool isVector; |
48 | union { |
49 | CMapVectorEntry *vector; |
50 | CID cid; |
51 | }; |
52 | }; |
53 | |
54 | //------------------------------------------------------------------------ |
55 | |
56 | static int getCharFromFile(void *data) |
57 | { |
58 | return fgetc(stream: (FILE *)data); |
59 | } |
60 | |
61 | static int getCharFromStream(void *data) |
62 | { |
63 | return ((Stream *)data)->getChar(); |
64 | } |
65 | |
66 | //------------------------------------------------------------------------ |
67 | |
68 | std::shared_ptr<CMap> CMap::parse(CMapCache *cache, const GooString *collectionA, Object *obj) |
69 | { |
70 | std::shared_ptr<CMap> cMap; |
71 | GooString *cMapNameA; |
72 | |
73 | if (obj->isName()) { |
74 | cMapNameA = new GooString(obj->getName()); |
75 | if (!(cMap = globalParams->getCMap(collection: collectionA, cMapName: cMapNameA))) { |
76 | error(category: errSyntaxError, pos: -1, msg: "Unknown CMap '{0:t}' for character collection '{1:t}'" , cMapNameA, collectionA); |
77 | } |
78 | delete cMapNameA; |
79 | } else if (obj->isStream()) { |
80 | if (!(cMap = CMap::parse(cache: nullptr, collectionA, str: obj->getStream()))) { |
81 | error(category: errSyntaxError, pos: -1, msg: "Invalid CMap in Type 0 font" ); |
82 | } |
83 | } else { |
84 | error(category: errSyntaxError, pos: -1, msg: "Invalid Encoding in Type 0 font" ); |
85 | return {}; |
86 | } |
87 | return cMap; |
88 | } |
89 | |
90 | std::shared_ptr<CMap> CMap::parse(CMapCache *cache, const GooString *collectionA, const GooString *cMapNameA) |
91 | { |
92 | FILE *f; |
93 | |
94 | if (!(f = globalParams->findCMapFile(collection: collectionA, cMapName: cMapNameA))) { |
95 | |
96 | // Check for an identity CMap. |
97 | if (!cMapNameA->cmp(sA: "Identity" ) || !cMapNameA->cmp(sA: "Identity-H" )) { |
98 | return std::shared_ptr<CMap>(new CMap(collectionA->copy(), cMapNameA->copy(), 0)); |
99 | } |
100 | if (!cMapNameA->cmp(sA: "Identity-V" )) { |
101 | return std::shared_ptr<CMap>(new CMap(collectionA->copy(), cMapNameA->copy(), 1)); |
102 | } |
103 | |
104 | error(category: errSyntaxError, pos: -1, msg: "Couldn't find '{0:t}' CMap file for '{1:t}' collection" , cMapNameA, collectionA); |
105 | return {}; |
106 | } |
107 | |
108 | auto cMap = std::shared_ptr<CMap>(new CMap(collectionA->copy(), cMapNameA->copy())); |
109 | cMap->parse2(cache, getCharFunc: &getCharFromFile, data: f); |
110 | |
111 | fclose(stream: f); |
112 | |
113 | return cMap; |
114 | } |
115 | |
116 | std::shared_ptr<CMap> CMap::parse(CMapCache *cache, const GooString *collectionA, Stream *str) |
117 | { |
118 | auto cMap = std::shared_ptr<CMap>(new CMap(collectionA->copy(), nullptr)); |
119 | Object obj1 = str->getDict()->lookup(key: "UseCMap" ); |
120 | if (!obj1.isNull()) { |
121 | cMap->useCMap(cache, obj: &obj1); |
122 | } |
123 | |
124 | str->reset(); |
125 | cMap->parse2(cache, getCharFunc: &getCharFromStream, data: str); |
126 | str->close(); |
127 | return cMap; |
128 | } |
129 | |
130 | void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data) |
131 | { |
132 | PSTokenizer *pst; |
133 | char tok1[256], tok2[256], tok3[256]; |
134 | int n1, n2, n3; |
135 | unsigned int start = 0, end = 0, code; |
136 | |
137 | pst = new PSTokenizer(getCharFunc, data); |
138 | pst->getToken(buf: tok1, size: sizeof(tok1), length: &n1); |
139 | while (pst->getToken(buf: tok2, size: sizeof(tok2), length: &n2)) { |
140 | if (!strcmp(s1: tok2, s2: "usecmap" )) { |
141 | if (tok1[0] == '/') { |
142 | useCMap(cache, useName: tok1 + 1); |
143 | } |
144 | pst->getToken(buf: tok1, size: sizeof(tok1), length: &n1); |
145 | } else if (!strcmp(s1: tok1, s2: "/WMode" )) { |
146 | wMode = atoi(nptr: tok2); |
147 | pst->getToken(buf: tok1, size: sizeof(tok1), length: &n1); |
148 | } else if (!strcmp(s1: tok2, s2: "begincidchar" )) { |
149 | while (pst->getToken(buf: tok1, size: sizeof(tok1), length: &n1)) { |
150 | if (!strcmp(s1: tok1, s2: "endcidchar" )) { |
151 | break; |
152 | } |
153 | if (!pst->getToken(buf: tok2, size: sizeof(tok2), length: &n2) || !strcmp(s1: tok2, s2: "endcidchar" )) { |
154 | error(category: errSyntaxError, pos: -1, msg: "Illegal entry in cidchar block in CMap" ); |
155 | break; |
156 | } |
157 | if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && n1 >= 4 && (n1 & 1) == 0)) { |
158 | error(category: errSyntaxError, pos: -1, msg: "Illegal entry in cidchar block in CMap" ); |
159 | continue; |
160 | } |
161 | tok1[n1 - 1] = '\0'; |
162 | if (sscanf(s: tok1 + 1, format: "%x" , &code) != 1) { |
163 | error(category: errSyntaxError, pos: -1, msg: "Illegal entry in cidchar block in CMap" ); |
164 | continue; |
165 | } |
166 | n1 = (n1 - 2) / 2; |
167 | addCIDs(start: code, end: code, nBytes: n1, firstCID: (CID)atoi(nptr: tok2)); |
168 | } |
169 | pst->getToken(buf: tok1, size: sizeof(tok1), length: &n1); |
170 | } else if (!strcmp(s1: tok2, s2: "begincidrange" )) { |
171 | while (pst->getToken(buf: tok1, size: sizeof(tok1), length: &n1)) { |
172 | if (!strcmp(s1: tok1, s2: "endcidrange" )) { |
173 | break; |
174 | } |
175 | if (!pst->getToken(buf: tok2, size: sizeof(tok2), length: &n2) || !strcmp(s1: tok2, s2: "endcidrange" ) || !pst->getToken(buf: tok3, size: sizeof(tok3), length: &n3) || !strcmp(s1: tok3, s2: "endcidrange" )) { |
176 | error(category: errSyntaxError, pos: -1, msg: "Illegal entry in cidrange block in CMap" ); |
177 | break; |
178 | } |
179 | if (tok1[0] == '<' && tok2[0] == '<' && n1 == n2 && n1 >= 4 && (n1 & 1) == 0) { |
180 | tok1[n1 - 1] = tok2[n1 - 1] = '\0'; |
181 | sscanf(s: tok1 + 1, format: "%x" , &start); |
182 | sscanf(s: tok2 + 1, format: "%x" , &end); |
183 | n1 = (n1 - 2) / 2; |
184 | addCIDs(start, end, nBytes: n1, firstCID: (CID)atoi(nptr: tok3)); |
185 | } |
186 | } |
187 | pst->getToken(buf: tok1, size: sizeof(tok1), length: &n1); |
188 | } else { |
189 | strcpy(dest: tok1, src: tok2); |
190 | } |
191 | } |
192 | delete pst; |
193 | } |
194 | |
195 | CMap::CMap(GooString *collectionA, GooString *cMapNameA) |
196 | { |
197 | int i; |
198 | |
199 | collection = collectionA; |
200 | cMapName = cMapNameA; |
201 | isIdent = false; |
202 | wMode = 0; |
203 | vector = (CMapVectorEntry *)gmallocn(count: 256, size: sizeof(CMapVectorEntry)); |
204 | for (i = 0; i < 256; ++i) { |
205 | vector[i].isVector = false; |
206 | vector[i].cid = 0; |
207 | } |
208 | } |
209 | |
210 | CMap::CMap(GooString *collectionA, GooString *cMapNameA, int wModeA) |
211 | { |
212 | collection = collectionA; |
213 | cMapName = cMapNameA; |
214 | isIdent = true; |
215 | wMode = wModeA; |
216 | vector = nullptr; |
217 | } |
218 | |
219 | void CMap::useCMap(CMapCache *cache, const char *useName) |
220 | { |
221 | GooString *useNameStr; |
222 | std::shared_ptr<CMap> subCMap; |
223 | |
224 | useNameStr = new GooString(useName); |
225 | // if cache is non-NULL, we already have a lock, and we can use |
226 | // CMapCache::getCMap() directly; otherwise, we need to use |
227 | // GlobalParams::getCMap() in order to acqure the lock need to use |
228 | // GlobalParams::getCMap |
229 | if (cache) { |
230 | subCMap = cache->getCMap(collection, cMapName: useNameStr); |
231 | } else { |
232 | subCMap = globalParams->getCMap(collection, cMapName: useNameStr); |
233 | } |
234 | delete useNameStr; |
235 | if (!subCMap) { |
236 | return; |
237 | } |
238 | isIdent = subCMap->isIdent; |
239 | if (subCMap->vector) { |
240 | copyVector(dest: vector, src: subCMap->vector); |
241 | } |
242 | } |
243 | |
244 | void CMap::useCMap(CMapCache *cache, Object *obj) |
245 | { |
246 | std::shared_ptr<CMap> subCMap = CMap::parse(cache, collectionA: collection, obj); |
247 | if (!subCMap) { |
248 | return; |
249 | } |
250 | isIdent = subCMap->isIdent; |
251 | if (subCMap->vector) { |
252 | copyVector(dest: vector, src: subCMap->vector); |
253 | } |
254 | } |
255 | |
256 | void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) |
257 | { |
258 | int i, j; |
259 | |
260 | for (i = 0; i < 256; ++i) { |
261 | if (src[i].isVector) { |
262 | if (!dest[i].isVector) { |
263 | dest[i].isVector = true; |
264 | dest[i].vector = (CMapVectorEntry *)gmallocn(count: 256, size: sizeof(CMapVectorEntry)); |
265 | for (j = 0; j < 256; ++j) { |
266 | dest[i].vector[j].isVector = false; |
267 | dest[i].vector[j].cid = 0; |
268 | } |
269 | } |
270 | copyVector(dest: dest[i].vector, src: src[i].vector); |
271 | } else { |
272 | if (dest[i].isVector) { |
273 | error(category: errSyntaxError, pos: -1, msg: "Collision in usecmap" ); |
274 | } else { |
275 | dest[i].cid = src[i].cid; |
276 | } |
277 | } |
278 | } |
279 | } |
280 | |
281 | void CMap::addCIDs(unsigned int start, unsigned int end, unsigned int nBytes, CID firstCID) |
282 | { |
283 | if (nBytes > 4) { |
284 | error(category: errSyntaxError, pos: -1, msg: "Illegal entry in cidchar block in CMap" ); |
285 | return; |
286 | } |
287 | |
288 | const unsigned int start1 = start & 0xffffff00; |
289 | const unsigned int end1 = end & 0xffffff00; |
290 | for (unsigned int i = start1; i <= end1; i += 0x100) { |
291 | CMapVectorEntry *vec = vector; |
292 | for (unsigned int j = nBytes - 1; j >= 1; --j) { |
293 | const int byte = (i >> (8 * j)) & 0xff; |
294 | if (!vec[byte].isVector) { |
295 | vec[byte].isVector = true; |
296 | vec[byte].vector = (CMapVectorEntry *)gmallocn(count: 256, size: sizeof(CMapVectorEntry)); |
297 | for (unsigned int k = 0; k < 256; ++k) { |
298 | vec[byte].vector[k].isVector = false; |
299 | vec[byte].vector[k].cid = 0; |
300 | } |
301 | } |
302 | vec = vec[byte].vector; |
303 | } |
304 | const int byte0 = (i < start) ? (start & 0xff) : 0; |
305 | const int byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff; |
306 | for (int byte = byte0; byte <= byte1; ++byte) { |
307 | if (vec[byte].isVector) { |
308 | error(category: errSyntaxError, pos: -1, msg: "Invalid CID ({0:ux} [{1:ud} bytes]) in CMap" , i, nBytes); |
309 | } else { |
310 | vec[byte].cid = firstCID + ((i + byte) - start); |
311 | } |
312 | } |
313 | } |
314 | } |
315 | |
316 | CMap::~CMap() |
317 | { |
318 | delete collection; |
319 | delete cMapName; |
320 | if (vector) { |
321 | freeCMapVector(vec: vector); |
322 | } |
323 | } |
324 | |
325 | void CMap::freeCMapVector(CMapVectorEntry *vec) |
326 | { |
327 | int i; |
328 | |
329 | for (i = 0; i < 256; ++i) { |
330 | if (vec[i].isVector) { |
331 | freeCMapVector(vec: vec[i].vector); |
332 | } |
333 | } |
334 | gfree(p: vec); |
335 | } |
336 | |
337 | bool CMap::match(const GooString *collectionA, const GooString *cMapNameA) |
338 | { |
339 | return !collection->cmp(str: collectionA) && !cMapName->cmp(str: cMapNameA); |
340 | } |
341 | |
342 | CID CMap::getCID(const char *s, int len, CharCode *c, int *nUsed) |
343 | { |
344 | CMapVectorEntry *vec; |
345 | CharCode cc; |
346 | int n, i; |
347 | |
348 | vec = vector; |
349 | cc = 0; |
350 | n = 0; |
351 | while (vec && n < len) { |
352 | i = s[n++] & 0xff; |
353 | cc = (cc << 8) | i; |
354 | if (!vec[i].isVector) { |
355 | *c = cc; |
356 | *nUsed = n; |
357 | return vec[i].cid; |
358 | } |
359 | vec = vec[i].vector; |
360 | } |
361 | if (isIdent && len >= 2) { |
362 | // identity CMap |
363 | *nUsed = 2; |
364 | *c = cc = ((s[0] & 0xff) << 8) + (s[1] & 0xff); |
365 | return cc; |
366 | } |
367 | *nUsed = 1; |
368 | *c = s[0] & 0xff; |
369 | return 0; |
370 | } |
371 | |
372 | void CMap::setReverseMapVector(unsigned int startCode, CMapVectorEntry *vec, unsigned int *rmap, unsigned int rmapSize, unsigned int ncand) |
373 | { |
374 | int i; |
375 | |
376 | if (vec == nullptr) { |
377 | return; |
378 | } |
379 | for (i = 0; i < 256; i++) { |
380 | if (vec[i].isVector) { |
381 | setReverseMapVector(startCode: (startCode + i) << 8, vec: vec[i].vector, rmap, rmapSize, ncand); |
382 | } else { |
383 | unsigned int cid = vec[i].cid; |
384 | |
385 | if (cid < rmapSize) { |
386 | unsigned int cand; |
387 | |
388 | for (cand = 0; cand < ncand; cand++) { |
389 | unsigned int code = startCode + i; |
390 | unsigned int idx = cid * ncand + cand; |
391 | if (rmap[idx] == 0) { |
392 | rmap[idx] = code; |
393 | break; |
394 | } else if (rmap[idx] == code) { |
395 | break; |
396 | } |
397 | } |
398 | } |
399 | } |
400 | } |
401 | } |
402 | |
403 | void CMap::setReverseMap(unsigned int *rmap, unsigned int rmapSize, unsigned int ncand) |
404 | { |
405 | setReverseMapVector(startCode: 0, vec: vector, rmap, rmapSize, ncand); |
406 | } |
407 | |
408 | //------------------------------------------------------------------------ |
409 | |
410 | CMapCache::CMapCache() { } |
411 | |
412 | std::shared_ptr<CMap> CMapCache::getCMap(const GooString *collection, const GooString *cMapName) |
413 | { |
414 | int i, j; |
415 | |
416 | if (cache[0] && cache[0]->match(collectionA: collection, cMapNameA: cMapName)) { |
417 | return cache[0]; |
418 | } |
419 | for (i = 1; i < cMapCacheSize; ++i) { |
420 | if (cache[i] && cache[i]->match(collectionA: collection, cMapNameA: cMapName)) { |
421 | std::shared_ptr<CMap> cmap = cache[i]; |
422 | for (j = i; j >= 1; --j) { |
423 | cache[j] = cache[j - 1]; |
424 | } |
425 | cache[0] = cmap; |
426 | return cmap; |
427 | } |
428 | } |
429 | std::shared_ptr<CMap> cmap = CMap::parse(cache: this, collectionA: collection, cMapNameA: cMapName); |
430 | if (cmap) { |
431 | for (j = cMapCacheSize - 1; j >= 1; --j) { |
432 | cache[j] = cache[j - 1]; |
433 | } |
434 | cache[0] = cmap; |
435 | return cmap; |
436 | } |
437 | return {}; |
438 | } |
439 | |