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
45struct CMapVectorEntry
46{
47 bool isVector;
48 union {
49 CMapVectorEntry *vector;
50 CID cid;
51 };
52};
53
54//------------------------------------------------------------------------
55
56static int getCharFromFile(void *data)
57{
58 return fgetc(stream: (FILE *)data);
59}
60
61static int getCharFromStream(void *data)
62{
63 return ((Stream *)data)->getChar();
64}
65
66//------------------------------------------------------------------------
67
68std::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
90std::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
116std::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
130void 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
195CMap::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
210CMap::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
219void 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
244void 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
256void 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
281void 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
316CMap::~CMap()
317{
318 delete collection;
319 delete cMapName;
320 if (vector) {
321 freeCMapVector(vec: vector);
322 }
323}
324
325void 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
337bool CMap::match(const GooString *collectionA, const GooString *cMapNameA)
338{
339 return !collection->cmp(str: collectionA) && !cMapName->cmp(str: cMapNameA);
340}
341
342CID 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
372void 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
403void 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
410CMapCache::CMapCache() { }
411
412std::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

source code of poppler/poppler/CMap.cc