1 | //======================================================================== |
2 | // |
3 | // GlobalParams.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) 2005 Martin Kretzschmar <martink@gnome.org> |
17 | // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com> |
18 | // Copyright (C) 2005, 2007-2010, 2012, 2015, 2017-2023 Albert Astals Cid <aacid@kde.org> |
19 | // Copyright (C) 2005 Jonathan Blandford <jrb@redhat.com> |
20 | // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net> |
21 | // Copyright (C) 2006 Takashi Iwai <tiwai@suse.de> |
22 | // Copyright (C) 2006 Ed Catmur <ed@catmur.co.uk> |
23 | // Copyright (C) 2007 Krzysztof Kowalczyk <kkowalczyk@gmail.com> |
24 | // Copyright (C) 2007, 2009 Jonathan Kew <jonathan_kew@sil.org> |
25 | // Copyright (C) 2009 Petr Gajdos <pgajdos@novell.com> |
26 | // Copyright (C) 2009, 2011, 2012, 2015 William Bader <williambader@hotmail.com> |
27 | // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net> |
28 | // Copyright (C) 2010, 2012 Hib Eris <hib@hiberis.nl> |
29 | // Copyright (C) 2010 Patrick Spendrin <ps_ml@gmx.de> |
30 | // Copyright (C) 2010 Jakub Wilk <jwilk@jwilk.net> |
31 | // Copyright (C) 2011 Pino Toscano <pino@kde.org> |
32 | // Copyright (C) 2011 Koji Otani <sho@bbr.jp> |
33 | // Copyright (C) 2012 Yi Yang <ahyangyi@gmail.com> |
34 | // Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com> |
35 | // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de> |
36 | // Copyright (C) 2012 Peter Breitenlohner <peb@mppmu.mpg.de> |
37 | // Copyright (C) 2013, 2014 Jason Crain <jason@aquaticape.us> |
38 | // Copyright (C) 2017 Christoph Cullmann <cullmann@kde.org> |
39 | // Copyright (C) 2017 Jean Ghali <jghali@libertysurf.fr> |
40 | // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
41 | // Copyright (C) 2018, 2020 Adam Reichold <adam.reichold@t-online.de> |
42 | // Copyright (C) 2019 Christian Persch <chpe@src.gnome.org> |
43 | // Copyright (C) 2019, 2024 Oliver Sander <oliver.sander@tu-dresden.de> |
44 | // Copyright (C) 2020 Kai Pastor <dg0yt@darc.de> |
45 | // Copyright (C) 2021, 2022 Stefan Löffler <st.loeffler@gmail.com> |
46 | // Copyright (C) 2021 sunderme <sunderme@gmx.de> |
47 | // Copyright (C) 2022 Even Rouault <even.rouault@spatialys.com> |
48 | // Copyright (C) 2022 Claes Nästén <pekdon@gmail.com> |
49 | // Copyright (C) 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
50 | // Copyright (C) 2023 Shivodit Gill <shivodit.gill@gmail.com> |
51 | // Copyright (C) 2024 Keyu Tao <me@taoky.moe> |
52 | // |
53 | // To see a description of the changes please see the Changelog file that |
54 | // came with your tarball or type make ChangeLog if you are building from git |
55 | // |
56 | //======================================================================== |
57 | |
58 | #include <config.h> |
59 | |
60 | #include <algorithm> |
61 | #include <cstring> |
62 | #include <cstdio> |
63 | #include <cctype> |
64 | #ifdef _WIN32 |
65 | # include <shlobj.h> |
66 | # include <mbstring.h> |
67 | #endif |
68 | #ifdef ANDROID |
69 | # include <android/font.h> |
70 | # include <android/font_matcher.h> |
71 | # include <android/system_fonts.h> |
72 | #endif |
73 | #include "goo/glibc.h" |
74 | #include "goo/gmem.h" |
75 | #include "goo/GooString.h" |
76 | #include "goo/gfile.h" |
77 | #include "goo/gdir.h" |
78 | #include "Error.h" |
79 | #include "NameToCharCode.h" |
80 | #include "CharCodeToUnicode.h" |
81 | #include "UnicodeMap.h" |
82 | #include "CMap.h" |
83 | #include "FontEncodingTables.h" |
84 | #include "GlobalParams.h" |
85 | #include "GfxFont.h" |
86 | |
87 | #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
88 | # include <fontconfig/fontconfig.h> |
89 | #endif |
90 | |
91 | #ifndef _MSC_VER |
92 | # include <strings.h> |
93 | #endif |
94 | |
95 | #ifndef FC_WEIGHT_BOOK |
96 | # define FC_WEIGHT_BOOK 75 |
97 | #endif |
98 | |
99 | #include "NameToUnicodeTable.h" |
100 | #include "UnicodeMapTables.h" |
101 | #include "UnicodeMapFuncs.h" |
102 | |
103 | #include "fofi/FoFiTrueType.h" |
104 | #include "fofi/FoFiIdentifier.h" |
105 | |
106 | //------------------------------------------------------------------------ |
107 | |
108 | #define cidToUnicodeCacheSize 4 |
109 | #define unicodeToUnicodeCacheSize 4 |
110 | |
111 | //------------------------------------------------------------------------ |
112 | |
113 | std::unique_ptr<GlobalParams> globalParams; |
114 | |
115 | #if defined(ENABLE_RELOCATABLE) && defined(_WIN32) |
116 | |
117 | /* search for data relative to where we are installed */ |
118 | |
119 | static HMODULE hmodule; |
120 | |
121 | extern "C" { |
122 | /* Provide declaration to squelch -Wmissing-declarations warning */ |
123 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); |
124 | |
125 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) |
126 | { |
127 | switch (fdwReason) { |
128 | case DLL_PROCESS_ATTACH: |
129 | hmodule = hinstDLL; |
130 | break; |
131 | } |
132 | |
133 | return TRUE; |
134 | } |
135 | } |
136 | |
137 | static std::string get_poppler_localdir(const std::string &suffix) |
138 | { |
139 | const std::string binSuffix("\\bin" ); |
140 | std::string retval(MAX_PATH, '\0'); |
141 | |
142 | if (!GetModuleFileNameA(hmodule, retval.data(), retval.size())) { |
143 | return POPPLER_DATADIR; |
144 | } |
145 | |
146 | const std::string::size_type p = retval.rfind('\\'); |
147 | if (p != std::string::npos) { |
148 | retval.erase(p); |
149 | if (retval.size() > binSuffix.size() && stricmp(retval.substr(p - binSuffix.size()).c_str(), binSuffix.c_str()) == 0) { |
150 | retval.erase(p - binSuffix.size()); |
151 | } |
152 | } |
153 | retval += suffix; |
154 | retval.shrink_to_fit(); |
155 | return retval; |
156 | } |
157 | |
158 | static const char *get_poppler_datadir(void) |
159 | { |
160 | static std::string retval; |
161 | static bool beenhere = false; |
162 | |
163 | if (!beenhere) { |
164 | retval = get_poppler_localdir("\\share\\poppler" ); |
165 | beenhere = true; |
166 | } |
167 | |
168 | return retval.c_str(); |
169 | } |
170 | |
171 | # undef POPPLER_DATADIR |
172 | # define POPPLER_DATADIR get_poppler_datadir() |
173 | |
174 | static const char *get_poppler_fontsdir(void) |
175 | { |
176 | static std::string retval; |
177 | static bool beenhere = false; |
178 | |
179 | if (!beenhere) { |
180 | retval = get_poppler_localdir("\\share\\fonts" ); |
181 | beenhere = true; |
182 | } |
183 | |
184 | return retval.c_str(); |
185 | } |
186 | # undef POPPLER_FONTSDIR |
187 | # define POPPLER_FONTSDIR get_poppler_fontsdir() |
188 | |
189 | #else |
190 | # define POPPLER_FONTSDIR nullptr |
191 | #endif |
192 | |
193 | //------------------------------------------------------------------------ |
194 | // SysFontInfo |
195 | //------------------------------------------------------------------------ |
196 | |
197 | class SysFontInfo |
198 | { |
199 | public: |
200 | GooString *name; |
201 | bool bold; |
202 | bool italic; |
203 | bool oblique; |
204 | bool fixedWidth; |
205 | GooString *path; |
206 | SysFontType type; |
207 | int fontNum; // for TrueType collections |
208 | GooString *substituteName; |
209 | |
210 | SysFontInfo(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, GooString *pathA, SysFontType typeA, int fontNumA, GooString *substituteNameA); |
211 | ~SysFontInfo(); |
212 | SysFontInfo(const SysFontInfo &) = delete; |
213 | SysFontInfo &operator=(const SysFontInfo &) = delete; |
214 | bool match(const SysFontInfo *fi) const; |
215 | bool match(const GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA) const; |
216 | bool match(const GooString *nameA, bool boldA, bool italicA) const; |
217 | }; |
218 | |
219 | SysFontInfo::SysFontInfo(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, GooString *pathA, SysFontType typeA, int fontNumA, GooString *substituteNameA) |
220 | { |
221 | name = nameA; |
222 | bold = boldA; |
223 | italic = italicA; |
224 | oblique = obliqueA; |
225 | fixedWidth = fixedWidthA; |
226 | path = pathA; |
227 | type = typeA; |
228 | fontNum = fontNumA; |
229 | substituteName = substituteNameA; |
230 | } |
231 | |
232 | SysFontInfo::~SysFontInfo() |
233 | { |
234 | delete name; |
235 | delete path; |
236 | delete substituteName; |
237 | } |
238 | |
239 | bool SysFontInfo::match(const SysFontInfo *fi) const |
240 | { |
241 | return !strcasecmp(s1: name->c_str(), s2: fi->name->c_str()) && bold == fi->bold && italic == fi->italic && oblique == fi->oblique && fixedWidth == fi->fixedWidth; |
242 | } |
243 | |
244 | bool SysFontInfo::match(const GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA) const |
245 | { |
246 | return !strcasecmp(s1: name->c_str(), s2: nameA->c_str()) && bold == boldA && italic == italicA && oblique == obliqueA && fixedWidth == fixedWidthA; |
247 | } |
248 | |
249 | bool SysFontInfo::match(const GooString *nameA, bool boldA, bool italicA) const |
250 | { |
251 | return !strcasecmp(s1: name->c_str(), s2: nameA->c_str()) && bold == boldA && italic == italicA; |
252 | } |
253 | |
254 | //------------------------------------------------------------------------ |
255 | // SysFontList |
256 | //------------------------------------------------------------------------ |
257 | |
258 | class SysFontList |
259 | { |
260 | public: |
261 | SysFontList(); |
262 | ~SysFontList(); |
263 | SysFontList(const SysFontList &) = delete; |
264 | SysFontList &operator=(const SysFontList &) = delete; |
265 | const SysFontInfo *find(const std::string &name, bool isFixedWidth, bool exact, const std::vector<std::string> &filesToIgnore = {}); |
266 | |
267 | const std::vector<SysFontInfo *> &getFonts() const { return fonts; } |
268 | |
269 | #ifdef _WIN32 |
270 | void scanWindowsFonts(const std::string &winFontDir); |
271 | #endif |
272 | #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
273 | void addFcFont(SysFontInfo *si) { fonts.push_back(x: si); } |
274 | #endif |
275 | private: |
276 | #ifdef _WIN32 |
277 | SysFontInfo *makeWindowsFont(const char *name, int fontNum, const char *path); |
278 | #endif |
279 | |
280 | std::vector<SysFontInfo *> fonts; |
281 | }; |
282 | |
283 | SysFontList::SysFontList() { } |
284 | |
285 | SysFontList::~SysFontList() |
286 | { |
287 | for (auto entry : fonts) { |
288 | delete entry; |
289 | } |
290 | } |
291 | |
292 | const SysFontInfo *SysFontList::find(const std::string &name, bool fixedWidth, bool exact, const std::vector<std::string> &filesToIgnore) |
293 | { |
294 | GooString *name2; |
295 | bool bold, italic, oblique; |
296 | int n; |
297 | |
298 | name2 = new GooString(name); |
299 | |
300 | // remove space, comma, dash chars |
301 | { |
302 | int i = 0; |
303 | while (i < name2->getLength()) { |
304 | const char c = name2->getChar(i); |
305 | if (c == ' ' || c == ',' || c == '-') { |
306 | name2->del(i); |
307 | } else { |
308 | ++i; |
309 | } |
310 | } |
311 | n = name2->getLength(); |
312 | } |
313 | |
314 | // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.) |
315 | if (n > 2 && !strcmp(s1: name2->c_str() + n - 2, s2: "MT" )) { |
316 | name2->del(i: n - 2, n: 2); |
317 | n -= 2; |
318 | } |
319 | |
320 | // look for "Regular" |
321 | if (n > 7 && !strcmp(s1: name2->c_str() + n - 7, s2: "Regular" )) { |
322 | name2->del(i: n - 7, n: 7); |
323 | n -= 7; |
324 | } |
325 | |
326 | // look for "Italic" |
327 | if (n > 6 && !strcmp(s1: name2->c_str() + n - 6, s2: "Italic" )) { |
328 | name2->del(i: n - 6, n: 6); |
329 | italic = true; |
330 | n -= 6; |
331 | } else { |
332 | italic = false; |
333 | } |
334 | |
335 | // look for "Oblique" |
336 | if (n > 6 && !strcmp(s1: name2->c_str() + n - 7, s2: "Oblique" )) { |
337 | name2->del(i: n - 7, n: 7); |
338 | oblique = true; |
339 | n -= 6; |
340 | } else { |
341 | oblique = false; |
342 | } |
343 | |
344 | // look for "Bold" |
345 | if (n > 4 && !strcmp(s1: name2->c_str() + n - 4, s2: "Bold" )) { |
346 | name2->del(i: n - 4, n: 4); |
347 | bold = true; |
348 | n -= 4; |
349 | } else { |
350 | bold = false; |
351 | } |
352 | |
353 | // remove trailing "MT" (FooMT-Bold, etc.) |
354 | if (n > 2 && !strcmp(s1: name2->c_str() + n - 2, s2: "MT" )) { |
355 | name2->del(i: n - 2, n: 2); |
356 | n -= 2; |
357 | } |
358 | |
359 | // remove trailing "PS" |
360 | if (n > 2 && !strcmp(s1: name2->c_str() + n - 2, s2: "PS" )) { |
361 | name2->del(i: n - 2, n: 2); |
362 | n -= 2; |
363 | } |
364 | |
365 | // remove trailing "IdentityH" |
366 | if (n > 9 && !strcmp(s1: name2->c_str() + n - 9, s2: "IdentityH" )) { |
367 | name2->del(i: n - 9, n: 9); |
368 | n -= 9; |
369 | } |
370 | |
371 | // search for the font |
372 | const SysFontInfo *fi = nullptr; |
373 | for (const SysFontInfo *f : fonts) { |
374 | fi = f; |
375 | if (fi->match(nameA: name2, boldA: bold, italicA: italic, obliqueA: oblique, fixedWidthA: fixedWidth)) { |
376 | if (std::find(first: filesToIgnore.begin(), last: filesToIgnore.end(), val: fi->path->toStr()) == filesToIgnore.end()) { |
377 | break; |
378 | } |
379 | } |
380 | fi = nullptr; |
381 | } |
382 | if (!fi && !exact && bold) { |
383 | // try ignoring the bold flag |
384 | for (const SysFontInfo *f : fonts) { |
385 | fi = f; |
386 | if (fi->match(nameA: name2, boldA: false, italicA: italic)) { |
387 | if (std::find(first: filesToIgnore.begin(), last: filesToIgnore.end(), val: fi->path->toStr()) == filesToIgnore.end()) { |
388 | break; |
389 | } |
390 | } |
391 | fi = nullptr; |
392 | } |
393 | } |
394 | if (!fi && !exact && (bold || italic)) { |
395 | // try ignoring the bold and italic flags |
396 | for (const SysFontInfo *f : fonts) { |
397 | fi = f; |
398 | if (fi->match(nameA: name2, boldA: false, italicA: false)) { |
399 | if (std::find(first: filesToIgnore.begin(), last: filesToIgnore.end(), val: fi->path->toStr()) == filesToIgnore.end()) { |
400 | break; |
401 | } |
402 | } |
403 | fi = nullptr; |
404 | } |
405 | } |
406 | |
407 | delete name2; |
408 | return fi; |
409 | } |
410 | |
411 | #define globalParamsLocker() const std::scoped_lock locker(mutex) |
412 | #define unicodeMapCacheLocker() const std::scoped_lock locker(unicodeMapCacheMutex) |
413 | #define cMapCacheLocker() const std::scoped_lock locker(cMapCacheMutex) |
414 | |
415 | //------------------------------------------------------------------------ |
416 | // parsing |
417 | //------------------------------------------------------------------------ |
418 | |
419 | GlobalParams::GlobalParams(const char *customPopplerDataDir) : popplerDataDir(customPopplerDataDir) |
420 | { |
421 | // scan the encoding in reverse because we want the lowest-numbered |
422 | // index for each char name ('space' is encoded twice) |
423 | macRomanReverseMap = new NameToCharCode(); |
424 | for (int i = 255; i >= 0; --i) { |
425 | if (macRomanEncoding[i]) { |
426 | macRomanReverseMap->add(name: macRomanEncoding[i], c: (CharCode)i); |
427 | } |
428 | } |
429 | |
430 | nameToUnicodeZapfDingbats = new NameToCharCode(); |
431 | nameToUnicodeText = new NameToCharCode(); |
432 | sysFonts = new SysFontList(); |
433 | textEncoding = new GooString("UTF-8" ); |
434 | printCommands = false; |
435 | profileCommands = false; |
436 | errQuiet = false; |
437 | |
438 | cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize); |
439 | unicodeToUnicodeCache = new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize); |
440 | unicodeMapCache = new UnicodeMapCache(); |
441 | cMapCache = new CMapCache(); |
442 | |
443 | utf8Map = nullptr; |
444 | |
445 | baseFontsInitialized = false; |
446 | |
447 | // set up the initial nameToUnicode tables |
448 | for (int i = 0; nameToUnicodeZapfDingbatsTab[i].name; ++i) { |
449 | nameToUnicodeZapfDingbats->add(name: nameToUnicodeZapfDingbatsTab[i].name, c: nameToUnicodeZapfDingbatsTab[i].u); |
450 | } |
451 | |
452 | for (int i = 0; nameToUnicodeTextTab[i].name; ++i) { |
453 | nameToUnicodeText->add(name: nameToUnicodeTextTab[i].name, c: nameToUnicodeTextTab[i].u); |
454 | } |
455 | |
456 | // set up the residentUnicodeMaps table |
457 | residentUnicodeMaps.reserve(n: 6); |
458 | UnicodeMap map = { "Latin1" , false, latin1UnicodeMapRanges, latin1UnicodeMapLen }; |
459 | residentUnicodeMaps.emplace(args: map.getEncodingName(), args: std::move(map)); |
460 | map = { "ASCII7" , false, ascii7UnicodeMapRanges, ascii7UnicodeMapLen }; |
461 | residentUnicodeMaps.emplace(args: map.getEncodingName(), args: std::move(map)); |
462 | map = { "Symbol" , false, symbolUnicodeMapRanges, symbolUnicodeMapLen }; |
463 | residentUnicodeMaps.emplace(args: map.getEncodingName(), args: std::move(map)); |
464 | map = { "ZapfDingbats" , false, zapfDingbatsUnicodeMapRanges, zapfDingbatsUnicodeMapLen }; |
465 | residentUnicodeMaps.emplace(args: map.getEncodingName(), args: std::move(map)); |
466 | map = { "UTF-8" , true, &mapUTF8 }; |
467 | residentUnicodeMaps.emplace(args: map.getEncodingName(), args: std::move(map)); |
468 | map = { "UTF-16" , true, &mapUTF16 }; |
469 | residentUnicodeMaps.emplace(args: map.getEncodingName(), args: std::move(map)); |
470 | |
471 | scanEncodingDirs(); |
472 | } |
473 | |
474 | void GlobalParams::scanEncodingDirs() |
475 | { |
476 | GDir *dir; |
477 | std::unique_ptr<GDirEntry> entry; |
478 | const char *dataRoot = popplerDataDir ? popplerDataDir : POPPLER_DATADIR; |
479 | |
480 | // allocate buffer large enough to append "/nameToUnicode" |
481 | size_t bufSize = strlen(s: dataRoot) + strlen(s: "/nameToUnicode" ) + 1; |
482 | char *dataPathBuffer = new char[bufSize]; |
483 | |
484 | snprintf(s: dataPathBuffer, maxlen: bufSize, format: "%s/nameToUnicode" , dataRoot); |
485 | dir = new GDir(dataPathBuffer, true); |
486 | while (entry = dir->getNextEntry(), entry != nullptr) { |
487 | if (!entry->isDir()) { |
488 | parseNameToUnicode(name: entry->getFullPath()); |
489 | } |
490 | } |
491 | delete dir; |
492 | |
493 | snprintf(s: dataPathBuffer, maxlen: bufSize, format: "%s/cidToUnicode" , dataRoot); |
494 | dir = new GDir(dataPathBuffer, false); |
495 | while (entry = dir->getNextEntry(), entry != nullptr) { |
496 | addCIDToUnicode(collection: entry->getName(), fileName: entry->getFullPath()); |
497 | } |
498 | delete dir; |
499 | |
500 | snprintf(s: dataPathBuffer, maxlen: bufSize, format: "%s/unicodeMap" , dataRoot); |
501 | dir = new GDir(dataPathBuffer, false); |
502 | while (entry = dir->getNextEntry(), entry != nullptr) { |
503 | addUnicodeMap(encodingName: entry->getName(), fileName: entry->getFullPath()); |
504 | } |
505 | delete dir; |
506 | |
507 | snprintf(s: dataPathBuffer, maxlen: bufSize, format: "%s/cMap" , dataRoot); |
508 | dir = new GDir(dataPathBuffer, false); |
509 | while (entry = dir->getNextEntry(), entry != nullptr) { |
510 | addCMapDir(collection: entry->getName(), dir: entry->getFullPath()); |
511 | toUnicodeDirs.push_back(x: entry->getFullPath()->copy()); |
512 | } |
513 | delete dir; |
514 | |
515 | delete[] dataPathBuffer; |
516 | } |
517 | |
518 | void GlobalParams::parseNameToUnicode(const GooString *name) |
519 | { |
520 | char *tok1, *tok2; |
521 | FILE *f; |
522 | char buf[256]; |
523 | int line; |
524 | Unicode u; |
525 | char *tokptr; |
526 | |
527 | if (!(f = openFile(path: name->c_str(), mode: "r" ))) { |
528 | error(category: errIO, pos: -1, msg: "Couldn't open 'nameToUnicode' file '{0:t}'" , name); |
529 | return; |
530 | } |
531 | line = 1; |
532 | while (getLine(buf, size: sizeof(buf), f)) { |
533 | tok1 = strtok_r(s: buf, delim: " \t\r\n" , save_ptr: &tokptr); |
534 | tok2 = strtok_r(s: nullptr, delim: " \t\r\n" , save_ptr: &tokptr); |
535 | if (tok1 && tok2) { |
536 | sscanf(s: tok1, format: "%x" , &u); |
537 | nameToUnicodeText->add(name: tok2, c: u); |
538 | } else { |
539 | error(category: errConfig, pos: -1, msg: "Bad line in 'nameToUnicode' file ({0:t}:{1:d})" , name, line); |
540 | } |
541 | ++line; |
542 | } |
543 | fclose(stream: f); |
544 | } |
545 | |
546 | void GlobalParams::addCIDToUnicode(const GooString *collection, const GooString *fileName) |
547 | { |
548 | cidToUnicodes[collection->toStr()] = fileName->toStr(); |
549 | } |
550 | |
551 | void GlobalParams::addUnicodeMap(const GooString *encodingName, const GooString *fileName) |
552 | { |
553 | unicodeMaps[encodingName->toStr()] = fileName->toStr(); |
554 | } |
555 | |
556 | void GlobalParams::addCMapDir(const GooString *collection, const GooString *dir) |
557 | { |
558 | cMapDirs.emplace(args: collection->toStr(), args: dir->toStr()); |
559 | } |
560 | |
561 | bool GlobalParams::parseYesNo2(const char *token, bool *flag) |
562 | { |
563 | if (!strcmp(s1: token, s2: "yes" )) { |
564 | *flag = true; |
565 | } else if (!strcmp(s1: token, s2: "no" )) { |
566 | *flag = false; |
567 | } else { |
568 | return false; |
569 | } |
570 | return true; |
571 | } |
572 | |
573 | GlobalParams::~GlobalParams() |
574 | { |
575 | delete macRomanReverseMap; |
576 | |
577 | delete nameToUnicodeZapfDingbats; |
578 | delete nameToUnicodeText; |
579 | for (auto entry : toUnicodeDirs) { |
580 | delete entry; |
581 | } |
582 | delete sysFonts; |
583 | delete textEncoding; |
584 | |
585 | delete cidToUnicodeCache; |
586 | delete unicodeToUnicodeCache; |
587 | delete unicodeMapCache; |
588 | delete cMapCache; |
589 | } |
590 | |
591 | //------------------------------------------------------------------------ |
592 | // accessors |
593 | //------------------------------------------------------------------------ |
594 | |
595 | CharCode GlobalParams::getMacRomanCharCode(const char *charName) |
596 | { |
597 | // no need to lock - macRomanReverseMap is constant |
598 | return macRomanReverseMap->lookup(name: charName); |
599 | } |
600 | |
601 | Unicode GlobalParams::mapNameToUnicodeAll(const char *charName) |
602 | { |
603 | // no need to lock - nameToUnicodeZapfDingbats and nameToUnicodeText are constant |
604 | Unicode u = nameToUnicodeZapfDingbats->lookup(name: charName); |
605 | if (!u) { |
606 | u = nameToUnicodeText->lookup(name: charName); |
607 | } |
608 | return u; |
609 | } |
610 | |
611 | Unicode GlobalParams::mapNameToUnicodeText(const char *charName) |
612 | { |
613 | // no need to lock - nameToUnicodeText is constant |
614 | return nameToUnicodeText->lookup(name: charName); |
615 | } |
616 | |
617 | UnicodeMap *GlobalParams::getResidentUnicodeMap(const std::string &encodingName) |
618 | { |
619 | UnicodeMap *map = nullptr; |
620 | |
621 | globalParamsLocker(); |
622 | const auto unicodeMap = residentUnicodeMaps.find(x: encodingName); |
623 | if (unicodeMap != residentUnicodeMaps.end()) { |
624 | map = &unicodeMap->second; |
625 | } |
626 | |
627 | return map; |
628 | } |
629 | |
630 | FILE *GlobalParams::getUnicodeMapFile(const std::string &encodingName) |
631 | { |
632 | FILE *file = nullptr; |
633 | |
634 | globalParamsLocker(); |
635 | const auto unicodeMap = unicodeMaps.find(x: encodingName); |
636 | if (unicodeMap != unicodeMaps.end()) { |
637 | file = openFile(path: unicodeMap->second.c_str(), mode: "r" ); |
638 | } |
639 | |
640 | return file; |
641 | } |
642 | |
643 | FILE *GlobalParams::findCMapFile(const GooString *collection, const GooString *cMapName) |
644 | { |
645 | FILE *file = nullptr; |
646 | |
647 | globalParamsLocker(); |
648 | const auto collectionCMapDirs = cMapDirs.equal_range(x: collection->toStr()); |
649 | for (auto cMapDir = collectionCMapDirs.first; cMapDir != collectionCMapDirs.second; ++cMapDir) { |
650 | auto *const path = new GooString(cMapDir->second); |
651 | appendToPath(path, fileName: cMapName->c_str()); |
652 | file = openFile(path: path->c_str(), mode: "r" ); |
653 | delete path; |
654 | if (file) { |
655 | break; |
656 | } |
657 | } |
658 | |
659 | return file; |
660 | } |
661 | |
662 | FILE *GlobalParams::findToUnicodeFile(const GooString *name) |
663 | { |
664 | GooString *fileName; |
665 | FILE *f; |
666 | |
667 | globalParamsLocker(); |
668 | for (const GooString *dir : toUnicodeDirs) { |
669 | fileName = appendToPath(path: dir->copy(), fileName: name->c_str()); |
670 | f = openFile(path: fileName->c_str(), mode: "r" ); |
671 | delete fileName; |
672 | if (f) { |
673 | return f; |
674 | } |
675 | } |
676 | return nullptr; |
677 | } |
678 | |
679 | #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
680 | static bool findModifier(const std::string &name, const size_t modStart, const char *modifier, size_t &start) |
681 | { |
682 | if (modStart == std::string::npos) { |
683 | return false; |
684 | } |
685 | |
686 | size_t match = name.find(s: modifier, pos: modStart); |
687 | if (match == std::string::npos) { |
688 | return false; |
689 | } else { |
690 | if (start == std::string::npos || match < start) { |
691 | start = match; |
692 | } |
693 | return true; |
694 | } |
695 | } |
696 | |
697 | static const char *getFontLang(const GfxFont *font) |
698 | { |
699 | const char *lang; |
700 | |
701 | // find the language we want the font to support |
702 | if (font->isCIDFont()) { |
703 | const GooString *collection = ((GfxCIDFont *)font)->getCollection(); |
704 | if (collection) { |
705 | if (strcmp(s1: collection->c_str(), s2: "Adobe-GB1" ) == 0) { |
706 | lang = "zh-cn" ; // Simplified Chinese |
707 | } else if (strcmp(s1: collection->c_str(), s2: "Adobe-CNS1" ) == 0) { |
708 | lang = "zh-tw" ; // Traditional Chinese |
709 | } else if (strcmp(s1: collection->c_str(), s2: "Adobe-Japan1" ) == 0) { |
710 | lang = "ja" ; // Japanese |
711 | } else if (strcmp(s1: collection->c_str(), s2: "Adobe-Japan2" ) == 0) { |
712 | lang = "ja" ; // Japanese |
713 | } else if (strcmp(s1: collection->c_str(), s2: "Adobe-Korea1" ) == 0) { |
714 | lang = "ko" ; // Korean |
715 | } else if (strcmp(s1: collection->c_str(), s2: "Adobe-UCS" ) == 0) { |
716 | lang = "xx" ; |
717 | } else if (strcmp(s1: collection->c_str(), s2: "Adobe-Identity" ) == 0) { |
718 | lang = "xx" ; |
719 | } else { |
720 | error(category: errUnimplemented, pos: -1, msg: "Unknown CID font collection: {0:t}. If this is expected to be a valid PDF document, please report to poppler bugtracker." , collection); |
721 | lang = "xx" ; |
722 | } |
723 | } else { |
724 | lang = "xx" ; |
725 | } |
726 | } else { |
727 | lang = "xx" ; |
728 | } |
729 | return lang; |
730 | } |
731 | |
732 | static FcPattern *buildFcPattern(const GfxFont *font, const GooString *base14Name) |
733 | { |
734 | int weight = -1, slant = -1, width = -1, spacing = -1; |
735 | FcPattern *p; |
736 | |
737 | // this is all heuristics will be overwritten if font had proper info |
738 | std::string fontName; |
739 | if (base14Name == nullptr) { |
740 | fontName = font->getNameWithoutSubsetTag(); |
741 | } else { |
742 | fontName = base14Name->toStr(); |
743 | } |
744 | |
745 | size_t modStart = fontName.find(c: ','); |
746 | if (modStart == std::string::npos) { |
747 | modStart = fontName.find(c: '-'); |
748 | } |
749 | |
750 | // remove the - from the names, for some reason, Fontconfig does not |
751 | // understand "MS-Mincho" but does with "MS Mincho" |
752 | std::replace(first: fontName.begin(), last: fontName.end(), old_value: '-', new_value: ' '); |
753 | |
754 | size_t start = std::string::npos; |
755 | findModifier(name: fontName, modStart, modifier: "Regular" , start); |
756 | findModifier(name: fontName, modStart, modifier: "Roman" , start); |
757 | |
758 | if (findModifier(name: fontName, modStart, modifier: "Oblique" , start)) { |
759 | slant = FC_SLANT_OBLIQUE; |
760 | } |
761 | if (findModifier(name: fontName, modStart, modifier: "Italic" , start)) { |
762 | slant = FC_SLANT_ITALIC; |
763 | } |
764 | if (findModifier(name: fontName, modStart, modifier: "Bold" , start)) { |
765 | weight = FC_WEIGHT_BOLD; |
766 | } |
767 | if (findModifier(name: fontName, modStart, modifier: "Light" , start)) { |
768 | weight = FC_WEIGHT_LIGHT; |
769 | } |
770 | if (findModifier(name: fontName, modStart, modifier: "Medium" , start)) { |
771 | weight = FC_WEIGHT_MEDIUM; |
772 | } |
773 | if (findModifier(name: fontName, modStart, modifier: "Condensed" , start)) { |
774 | width = FC_WIDTH_CONDENSED; |
775 | } |
776 | |
777 | std::string family; |
778 | if (start == std::string::npos) { |
779 | family = fontName; |
780 | } else { |
781 | // There have been "modifiers" in the name, crop them to obtain |
782 | // the family name |
783 | family = fontName.substr(pos: 0, n: modStart); |
784 | } |
785 | |
786 | // use font flags |
787 | if (font->isFixedWidth()) { |
788 | spacing = FC_MONO; |
789 | } |
790 | if (font->isBold()) { |
791 | weight = FC_WEIGHT_BOLD; |
792 | } |
793 | if (font->isItalic()) { |
794 | slant = FC_SLANT_ITALIC; |
795 | } |
796 | |
797 | // if the FontDescriptor specified a family name use it |
798 | if (font->getFamily()) { |
799 | family = font->getFamily()->toStr(); |
800 | } |
801 | |
802 | // if the FontDescriptor specified a weight use it |
803 | switch (font->getWeight()) { |
804 | case GfxFont::W100: |
805 | weight = FC_WEIGHT_EXTRALIGHT; |
806 | break; |
807 | case GfxFont::W200: |
808 | weight = FC_WEIGHT_LIGHT; |
809 | break; |
810 | case GfxFont::W300: |
811 | weight = FC_WEIGHT_BOOK; |
812 | break; |
813 | case GfxFont::W400: |
814 | weight = FC_WEIGHT_NORMAL; |
815 | break; |
816 | case GfxFont::W500: |
817 | weight = FC_WEIGHT_MEDIUM; |
818 | break; |
819 | case GfxFont::W600: |
820 | weight = FC_WEIGHT_DEMIBOLD; |
821 | break; |
822 | case GfxFont::W700: |
823 | weight = FC_WEIGHT_BOLD; |
824 | break; |
825 | case GfxFont::W800: |
826 | weight = FC_WEIGHT_EXTRABOLD; |
827 | break; |
828 | case GfxFont::W900: |
829 | weight = FC_WEIGHT_BLACK; |
830 | break; |
831 | default: |
832 | break; |
833 | } |
834 | |
835 | // if the FontDescriptor specified a width use it |
836 | switch (font->getStretch()) { |
837 | case GfxFont::UltraCondensed: |
838 | width = FC_WIDTH_ULTRACONDENSED; |
839 | break; |
840 | case GfxFont::ExtraCondensed: |
841 | width = FC_WIDTH_EXTRACONDENSED; |
842 | break; |
843 | case GfxFont::Condensed: |
844 | width = FC_WIDTH_CONDENSED; |
845 | break; |
846 | case GfxFont::SemiCondensed: |
847 | width = FC_WIDTH_SEMICONDENSED; |
848 | break; |
849 | case GfxFont::Normal: |
850 | width = FC_WIDTH_NORMAL; |
851 | break; |
852 | case GfxFont::SemiExpanded: |
853 | width = FC_WIDTH_SEMIEXPANDED; |
854 | break; |
855 | case GfxFont::Expanded: |
856 | width = FC_WIDTH_EXPANDED; |
857 | break; |
858 | case GfxFont::ExtraExpanded: |
859 | width = FC_WIDTH_EXTRAEXPANDED; |
860 | break; |
861 | case GfxFont::UltraExpanded: |
862 | width = FC_WIDTH_ULTRAEXPANDED; |
863 | break; |
864 | default: |
865 | break; |
866 | } |
867 | |
868 | const char *lang = getFontLang(font); |
869 | |
870 | p = FcPatternBuild(p: nullptr, FC_FAMILY, FcTypeString, family.c_str(), FC_LANG, FcTypeString, lang, NULL); |
871 | if (slant != -1) { |
872 | FcPatternAddInteger(p, FC_SLANT, i: slant); |
873 | } |
874 | if (weight != -1) { |
875 | FcPatternAddInteger(p, FC_WEIGHT, i: weight); |
876 | } |
877 | if (width != -1) { |
878 | FcPatternAddInteger(p, FC_WIDTH, i: width); |
879 | } |
880 | if (spacing != -1) { |
881 | FcPatternAddInteger(p, FC_SPACING, i: spacing); |
882 | } |
883 | |
884 | return p; |
885 | } |
886 | #endif |
887 | |
888 | GooString *GlobalParams::findFontFile(const std::string &fontName) |
889 | { |
890 | GooString *path = nullptr; |
891 | |
892 | setupBaseFonts(POPPLER_FONTSDIR); |
893 | globalParamsLocker(); |
894 | const auto fontFile = fontFiles.find(x: fontName); |
895 | if (fontFile != fontFiles.end()) { |
896 | path = new GooString(fontFile->second); |
897 | } |
898 | |
899 | return path; |
900 | } |
901 | |
902 | #if defined(WITH_FONTCONFIGURATION_FONTCONFIG) || defined(WITH_FONTCONFIGURATION_WIN32) |
903 | |
904 | static bool supportedFontForEmbedding(Unicode uChar, const char *filepath, int faceIndex) |
905 | { |
906 | if (!std::string_view(filepath).ends_with(x: ".ttf" ) && !std::string_view(filepath).ends_with(x: ".ttc" ) && !std::string_view(filepath).ends_with(x: ".otf" )) { |
907 | // for now we only support ttf, ttc, otf fonts |
908 | return false; |
909 | } |
910 | |
911 | const FoFiIdentifierType fontFoFiType = FoFiIdentifier::identifyFile(fileName: filepath); |
912 | if (fontFoFiType != fofiIdTrueType && fontFoFiType != fofiIdTrueTypeCollection && fontFoFiType != fofiIdOpenTypeCFF8Bit && fontFoFiType != fofiIdOpenTypeCFFCID) { |
913 | // for now we only support ttf, ttc, otf fonts |
914 | return false; |
915 | } |
916 | |
917 | const std::unique_ptr<FoFiTrueType> fft = FoFiTrueType::load(fileName: filepath, faceIndexA: faceIndex); |
918 | if (!fft) { |
919 | error(category: errIO, pos: -1, msg: "Form::addFontToDefaultResources. Failed to FoFiTrueType::load {0:s}" , filepath); |
920 | return false; |
921 | } |
922 | |
923 | // Look for the Unicode BMP cmaps, which are 0/3 or 3/1 |
924 | int unicodeBMPCMap = fft->findCmap(platform: 0, encoding: 3); |
925 | if (unicodeBMPCMap < 0) { |
926 | unicodeBMPCMap = fft->findCmap(platform: 3, encoding: 1); |
927 | } |
928 | if (unicodeBMPCMap < 0) { |
929 | // for now we only support files with unicode bmp cmaps |
930 | return false; |
931 | } |
932 | |
933 | const int glyph = fft->mapCodeToGID(i: unicodeBMPCMap, c: uChar); |
934 | return glyph > 0; |
935 | } |
936 | |
937 | #endif |
938 | |
939 | /* if you can't or don't want to use Fontconfig, you need to implement |
940 | this function for your platform. For Windows, it's in GlobalParamsWin.cc |
941 | */ |
942 | #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
943 | // not needed for fontconfig |
944 | void GlobalParams::setupBaseFonts(const char *) { } |
945 | |
946 | GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString *substituteFontName) |
947 | { |
948 | SysFontType type; |
949 | int fontNum; |
950 | |
951 | return findSystemFontFile(font, type: &type, fontNum: &fontNum, substituteFontName, base14Name); |
952 | } |
953 | |
954 | GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) |
955 | { |
956 | const SysFontInfo *fi = nullptr; |
957 | FcPattern *p = nullptr; |
958 | GooString *path = nullptr; |
959 | const std::optional<std::string> &fontName = font->getName(); |
960 | GooString substituteName; |
961 | if (!fontName) { |
962 | return nullptr; |
963 | } |
964 | |
965 | globalParamsLocker(); |
966 | |
967 | if ((fi = sysFonts->find(name: *fontName, fixedWidth: font->isFixedWidth(), exact: true))) { |
968 | path = fi->path->copy(); |
969 | *type = fi->type; |
970 | *fontNum = fi->fontNum; |
971 | substituteName.Set(fi->substituteName->c_str()); |
972 | } else { |
973 | FcChar8 *s; |
974 | char *ext; |
975 | FcResult res; |
976 | FcFontSet *set; |
977 | int i; |
978 | FcLangSet *lb = nullptr; |
979 | p = buildFcPattern(font, base14Name); |
980 | |
981 | if (!p) { |
982 | goto fin; |
983 | } |
984 | FcConfigSubstitute(config: nullptr, p, kind: FcMatchPattern); |
985 | FcDefaultSubstitute(pattern: p); |
986 | set = FcFontSort(config: nullptr, p, FcFalse, csp: nullptr, result: &res); |
987 | if (!set) { |
988 | goto fin; |
989 | } |
990 | |
991 | // find the language we want the font to support |
992 | const char *lang = getFontLang(font); |
993 | if (strcmp(s1: lang, s2: "xx" ) != 0) { |
994 | lb = FcLangSetCreate(); |
995 | FcLangSetAdd(ls: lb, lang: (FcChar8 *)lang); |
996 | } |
997 | |
998 | /* |
999 | scan twice. |
1000 | first: fonts support the language |
1001 | second: all fonts (fall back) |
1002 | */ |
1003 | while (fi == nullptr) { |
1004 | for (i = 0; i < set->nfont; ++i) { |
1005 | res = FcPatternGetString(p: set->fonts[i], FC_FILE, n: 0, s: &s); |
1006 | if (res != FcResultMatch || !s) { |
1007 | continue; |
1008 | } |
1009 | if (lb != nullptr) { |
1010 | FcLangSet *l; |
1011 | res = FcPatternGetLangSet(p: set->fonts[i], FC_LANG, n: 0, ls: &l); |
1012 | if (res != FcResultMatch || !FcLangSetContains(lsa: l, lsb: lb)) { |
1013 | continue; |
1014 | } |
1015 | } |
1016 | FcChar8 *s2; |
1017 | res = FcPatternGetString(p: set->fonts[i], FC_FULLNAME, n: 0, s: &s2); |
1018 | if (res == FcResultMatch && s2) { |
1019 | substituteName.Set((char *)s2); |
1020 | } else { |
1021 | // fontconfig does not extract fullname for some fonts |
1022 | // create the fullname from family and style |
1023 | res = FcPatternGetString(p: set->fonts[i], FC_FAMILY, n: 0, s: &s2); |
1024 | if (res == FcResultMatch && s2) { |
1025 | substituteName.Set((char *)s2); |
1026 | res = FcPatternGetString(p: set->fonts[i], FC_STYLE, n: 0, s: &s2); |
1027 | if (res == FcResultMatch && s2) { |
1028 | GooString *style = new GooString((char *)s2); |
1029 | if (style->cmp(sA: "Regular" ) != 0) { |
1030 | substituteName.append(str: " " ); |
1031 | substituteName.append(str: style); |
1032 | } |
1033 | delete style; |
1034 | } |
1035 | } |
1036 | } |
1037 | ext = strrchr(s: (char *)s, c: '.'); |
1038 | if (!ext) { |
1039 | continue; |
1040 | } |
1041 | if (!strncasecmp(s1: ext, s2: ".ttf" , n: 4) || !strncasecmp(s1: ext, s2: ".ttc" , n: 4) || !strncasecmp(s1: ext, s2: ".otf" , n: 4)) { |
1042 | int weight, slant; |
1043 | bool bold = font->isBold(); |
1044 | bool italic = font->isItalic(); |
1045 | bool oblique = false; |
1046 | FcPatternGetInteger(p: set->fonts[i], FC_WEIGHT, n: 0, i: &weight); |
1047 | FcPatternGetInteger(p: set->fonts[i], FC_SLANT, n: 0, i: &slant); |
1048 | if (weight == FC_WEIGHT_DEMIBOLD || weight == FC_WEIGHT_BOLD || weight == FC_WEIGHT_EXTRABOLD || weight == FC_WEIGHT_BLACK) { |
1049 | bold = true; |
1050 | } |
1051 | if (slant == FC_SLANT_ITALIC) { |
1052 | italic = true; |
1053 | } |
1054 | if (slant == FC_SLANT_OBLIQUE) { |
1055 | oblique = true; |
1056 | } |
1057 | *fontNum = 0; |
1058 | *type = (!strncasecmp(s1: ext, s2: ".ttc" , n: 4)) ? sysFontTTC : sysFontTTF; |
1059 | FcPatternGetInteger(p: set->fonts[i], FC_INDEX, n: 0, i: fontNum); |
1060 | SysFontInfo *sfi = new SysFontInfo(new GooString(*fontName), bold, italic, oblique, font->isFixedWidth(), new GooString((char *)s), *type, *fontNum, substituteName.copy()); |
1061 | sysFonts->addFcFont(si: sfi); |
1062 | fi = sfi; |
1063 | path = new GooString((char *)s); |
1064 | } else if (!strncasecmp(s1: ext, s2: ".pfa" , n: 4) || !strncasecmp(s1: ext, s2: ".pfb" , n: 4)) { |
1065 | int weight, slant; |
1066 | bool bold = font->isBold(); |
1067 | bool italic = font->isItalic(); |
1068 | bool oblique = false; |
1069 | FcPatternGetInteger(p: set->fonts[i], FC_WEIGHT, n: 0, i: &weight); |
1070 | FcPatternGetInteger(p: set->fonts[i], FC_SLANT, n: 0, i: &slant); |
1071 | if (weight == FC_WEIGHT_DEMIBOLD || weight == FC_WEIGHT_BOLD || weight == FC_WEIGHT_EXTRABOLD || weight == FC_WEIGHT_BLACK) { |
1072 | bold = true; |
1073 | } |
1074 | if (slant == FC_SLANT_ITALIC) { |
1075 | italic = true; |
1076 | } |
1077 | if (slant == FC_SLANT_OBLIQUE) { |
1078 | oblique = true; |
1079 | } |
1080 | *fontNum = 0; |
1081 | *type = (!strncasecmp(s1: ext, s2: ".pfa" , n: 4)) ? sysFontPFA : sysFontPFB; |
1082 | FcPatternGetInteger(p: set->fonts[i], FC_INDEX, n: 0, i: fontNum); |
1083 | SysFontInfo *sfi = new SysFontInfo(new GooString(*fontName), bold, italic, oblique, font->isFixedWidth(), new GooString((char *)s), *type, *fontNum, substituteName.copy()); |
1084 | sysFonts->addFcFont(si: sfi); |
1085 | fi = sfi; |
1086 | path = new GooString((char *)s); |
1087 | } else { |
1088 | continue; |
1089 | } |
1090 | break; |
1091 | } |
1092 | if (lb != nullptr) { |
1093 | FcLangSetDestroy(ls: lb); |
1094 | lb = nullptr; |
1095 | } else { |
1096 | /* scan all fonts of the list */ |
1097 | break; |
1098 | } |
1099 | } |
1100 | FcFontSetDestroy(s: set); |
1101 | } |
1102 | if (path == nullptr && (fi = sysFonts->find(name: *fontName, fixedWidth: font->isFixedWidth(), exact: false))) { |
1103 | path = fi->path->copy(); |
1104 | *type = fi->type; |
1105 | *fontNum = fi->fontNum; |
1106 | } |
1107 | if (substituteFontName) { |
1108 | substituteFontName->Set(substituteName.c_str()); |
1109 | } |
1110 | fin: |
1111 | if (p) { |
1112 | FcPatternDestroy(p); |
1113 | } |
1114 | |
1115 | return path; |
1116 | } |
1117 | |
1118 | FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle, const std::vector<std::string> &filesToIgnore) |
1119 | { |
1120 | FcPattern *p = FcPatternBuild(p: nullptr, FC_FAMILY, FcTypeString, fontFamily.c_str(), FC_STYLE, FcTypeString, fontStyle.c_str(), nullptr); |
1121 | FcConfigSubstitute(config: nullptr, p, kind: FcMatchPattern); |
1122 | FcDefaultSubstitute(pattern: p); |
1123 | if (p) { |
1124 | const std::unique_ptr<FcPattern, void (*)(FcPattern *)> pDeleter(p, [](FcPattern *pattern) { FcPatternDestroy(p: pattern); }); |
1125 | FcResult res; |
1126 | FcFontSet *fontSet = FcFontSort(config: nullptr, p, FcFalse, csp: nullptr, result: &res); |
1127 | if (fontSet) { |
1128 | const std::unique_ptr<FcFontSet, void (*)(FcFontSet *)> fontSetDeleter(fontSet, [](FcFontSet *fSet) { FcFontSetDestroy(s: fSet); }); |
1129 | if (res == FcResultMatch) { |
1130 | for (int i = 0; i < fontSet->nfont; i++) { |
1131 | FcChar8 *fcFilePath = nullptr; |
1132 | int faceIndex = 0; |
1133 | FcPatternGetString(p: fontSet->fonts[i], FC_FILE, n: 0, s: &fcFilePath); |
1134 | FcPatternGetInteger(p: fontSet->fonts[i], FC_INDEX, n: 0, i: &faceIndex); |
1135 | |
1136 | const std::string sFilePath = reinterpret_cast<char *>(fcFilePath); |
1137 | if (std::find(first: filesToIgnore.begin(), last: filesToIgnore.end(), val: sFilePath) == filesToIgnore.end()) { |
1138 | return FamilyStyleFontSearchResult(sFilePath, faceIndex); |
1139 | } |
1140 | } |
1141 | } |
1142 | } |
1143 | } |
1144 | |
1145 | error(category: errIO, pos: -1, msg: "Couldn't find font file for {0:s} {1:s}" , fontFamily.c_str(), fontStyle.c_str()); |
1146 | return {}; |
1147 | } |
1148 | |
1149 | UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate) |
1150 | { |
1151 | FcPattern *pattern = buildFcPattern(font: &fontToEmulate, base14Name: nullptr); |
1152 | |
1153 | FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern); |
1154 | FcDefaultSubstitute(pattern); |
1155 | |
1156 | FcResult result = FcResultMatch; |
1157 | FcFontSet *fontSet = FcFontSort(config: nullptr, p: pattern, FcFalse, csp: nullptr, result: &result); |
1158 | FcPatternDestroy(p: pattern); |
1159 | |
1160 | if (fontSet) { |
1161 | const std::unique_ptr<FcFontSet, void (*)(FcFontSet *)> fontSetDeleter(fontSet, [](FcFontSet *fSet) { FcFontSetDestroy(s: fSet); }); |
1162 | for (int i = 0; i < fontSet->nfont; i++) { |
1163 | FcChar8 *fcFilePath = nullptr; |
1164 | int faceIndex = 0; |
1165 | FcChar8 *fcFamily = nullptr; |
1166 | FcChar8 *fcStyle = nullptr; |
1167 | FcCharSet *fcCharSet = nullptr; |
1168 | FcPatternGetString(p: fontSet->fonts[i], FC_FILE, n: 0, s: &fcFilePath); |
1169 | FcPatternGetInteger(p: fontSet->fonts[i], FC_INDEX, n: 0, i: &faceIndex); |
1170 | FcPatternGetString(p: fontSet->fonts[i], FC_FAMILY, n: 0, s: &fcFamily); |
1171 | FcPatternGetString(p: fontSet->fonts[i], FC_STYLE, n: 0, s: &fcStyle); |
1172 | FcPatternGetCharSet(p: fontSet->fonts[i], FC_CHARSET, n: 0, c: &fcCharSet); |
1173 | if (!fcFilePath || !fcFamily || !fcStyle || !fcCharSet) { |
1174 | continue; |
1175 | } |
1176 | if (!FcCharSetHasChar(fcs: fcCharSet, ucs4: uChar)) { |
1177 | continue; |
1178 | } |
1179 | |
1180 | const char *filepath = reinterpret_cast<char *>(fcFilePath); |
1181 | |
1182 | if (supportedFontForEmbedding(uChar, filepath, faceIndex)) { |
1183 | return UCharFontSearchResult(filepath, faceIndex, reinterpret_cast<char *>(fcFamily), reinterpret_cast<char *>(fcStyle)); |
1184 | } |
1185 | } |
1186 | } |
1187 | |
1188 | return {}; |
1189 | } |
1190 | #elif defined(WITH_FONTCONFIGURATION_ANDROID) |
1191 | // Uses the font file mapping created by GlobalParams::setupBaseFonts |
1192 | // to return the path to a base-14 font file |
1193 | GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString *substituteFontName) |
1194 | { |
1195 | return findFontFile(base14Name->toStr()); |
1196 | } |
1197 | |
1198 | # if __ANDROID_API__ >= 29 |
1199 | |
1200 | // This struct is used by the AFontMatcher unique_ptr for destroying the |
1201 | // AFontMatcher object |
1202 | struct AFontMatcherDestroyer |
1203 | { |
1204 | void operator()(AFontMatcher *fontmatcher) { AFontMatcher_destroy(fontmatcher); } |
1205 | }; |
1206 | |
1207 | // This struct is used by the AFontMatcher unique_ptr for destroying the |
1208 | // AFont object |
1209 | struct AFontDestroyer |
1210 | { |
1211 | void operator()(AFont *afont) { AFont_close(afont); } |
1212 | }; |
1213 | |
1214 | # endif |
1215 | |
1216 | GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) |
1217 | { |
1218 | GooString *path = nullptr; |
1219 | const std::optional<std::string> &fontName = font->getName(); |
1220 | |
1221 | if (!fontName) { |
1222 | return nullptr; |
1223 | } |
1224 | |
1225 | globalParamsLocker(); |
1226 | |
1227 | # if __ANDROID_API__ >= 29 |
1228 | // If font is not found in the default base-14 fonts, |
1229 | // use Android-NDK's AFontMatcher API instead. |
1230 | // Documentation for AFontMatcher API can be found at: |
1231 | // https://developer.android.com/ndk/reference/group/font |
1232 | std::string genericFontFamily = "serif" ; |
1233 | |
1234 | if (!font->isSerif()) { |
1235 | genericFontFamily = "sans-serif" ; |
1236 | } else if (font->isFixedWidth()) { |
1237 | genericFontFamily = "monospace" ; |
1238 | } |
1239 | |
1240 | std::unique_ptr<AFontMatcher, AFontMatcherDestroyer> fontmatcher { AFontMatcher_create() }; |
1241 | |
1242 | // Set font weight and italics for the font. |
1243 | AFontMatcher_setStyle(fontmatcher.get(), font->getWeight() * 100, font->isItalic()); |
1244 | |
1245 | // Get font match and the font file's path |
1246 | std::unique_ptr<AFont, AFontDestroyer> afont { AFontMatcher_match(fontmatcher.get(), genericFontFamily.c_str(), (uint16_t *)u"A" , 1, nullptr) }; |
1247 | path = new GooString(AFont_getFontFilePath(afont.get())); |
1248 | |
1249 | // Set the type of font. Fonts returned by AFontMatcher are of |
1250 | // four possible types - ttf, otf, ttc, otc. |
1251 | if (path->ends_with(".ttf" ) || path->ends_with(".otf" )) { |
1252 | *type = sysFontTTF; |
1253 | } else if (path->ends_with(".ttc" ) || path->ends_with(".otc" )) { |
1254 | *type = sysFontTTC; |
1255 | } |
1256 | # else |
1257 | # pragma message("Compiling without AFontMatcher API due to Android API version being lower than 29.") |
1258 | # endif |
1259 | |
1260 | return path; |
1261 | } |
1262 | |
1263 | static struct |
1264 | { |
1265 | const char *name; |
1266 | const char *otFileName; |
1267 | } displayFontTab[] = { { "Courier" , "NimbusMonoPS-Regular.otf" }, |
1268 | { "Courier-Bold" , "NimbusMonoPS-Bold.otf" }, |
1269 | { "Courier-BoldOblique" , "NimbusMonoPS-BoldItalic.otf" }, |
1270 | { "Courier-Oblique" , "NimbusMonoPS-Italic.otf" }, |
1271 | { "Helvetica" , "NimbusSans-Regular.otf" }, |
1272 | { "Helvetica-Bold" , "NimbusSans-Bold.otf" }, |
1273 | { "Helvetica-BoldOblique" , "NimbusSans-BoldItalic.otf" }, |
1274 | { "Helvetica-Oblique" , "NimbusSans-Italic.otf" }, |
1275 | { "Symbol" , "StandardSymbolsPS.otf" }, |
1276 | { "Times-Bold" , "NimbusRoman-Bold.otf" }, |
1277 | { "Times-BoldItalic" , "NimbusRoman-BoldItalic.otf" }, |
1278 | { "Times-Italic" , "NimbusRoman-Italic.otf" }, |
1279 | { "Times-Roman" , "NimbusRoman-Regular.otf" }, |
1280 | { "ZapfDingbats" , "D050000L.otf" }, |
1281 | { nullptr, nullptr } }; |
1282 | |
1283 | // The path to the font directory. Set by GlobalParams::setFontDir() |
1284 | static std::string displayFontDir; |
1285 | |
1286 | // This method creates a mapping from base-14 font names to their |
1287 | // paths on the file system. On Android, it searches within the |
1288 | // directory set by GlobalParams::setFontDir(). |
1289 | void GlobalParams::setupBaseFonts(const char *dir) |
1290 | { |
1291 | FILE *f; |
1292 | int i; |
1293 | |
1294 | for (i = 0; displayFontTab[i].name; ++i) { |
1295 | if (fontFiles.count(displayFontTab[i].name) > 0) { |
1296 | continue; |
1297 | } |
1298 | |
1299 | std::unique_ptr<GooString> fontName = std::make_unique<GooString>(displayFontTab[i].name); |
1300 | std::unique_ptr<GooString> fileName; |
1301 | if (dir) { |
1302 | fileName.reset(appendToPath(new GooString(dir), displayFontTab[i].otFileName)); |
1303 | if ((f = openFile(fileName->c_str(), "rb" ))) { |
1304 | fclose(f); |
1305 | } else { |
1306 | fileName.reset(); |
1307 | } |
1308 | } |
1309 | if (!displayFontDir.empty()) { |
1310 | fileName.reset(appendToPath(new GooString(displayFontDir), displayFontTab[i].otFileName)); |
1311 | if ((f = openFile(fileName->c_str(), "rb" ))) { |
1312 | fclose(f); |
1313 | } else { |
1314 | fileName.reset(); |
1315 | } |
1316 | } |
1317 | if (!fileName) { |
1318 | error(errConfig, -1, "No display font for '{0:s}'" , displayFontTab[i].name); |
1319 | continue; |
1320 | } |
1321 | addFontFile(fontName->toStr(), fileName->toStr()); |
1322 | } |
1323 | } |
1324 | |
1325 | FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle, const std::vector<std::string> &filesToIgnore) |
1326 | { |
1327 | error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForFamilyAndStyle not implemented for this platform" ); |
1328 | return {}; |
1329 | } |
1330 | |
1331 | UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate) |
1332 | { |
1333 | error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForUChar not implemented for this platform" ); |
1334 | return {}; |
1335 | } |
1336 | |
1337 | #elif defined(WITH_FONTCONFIGURATION_WIN32) |
1338 | # include "GlobalParamsWin.cc" |
1339 | |
1340 | GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString * /*substituteFontName*/) |
1341 | { |
1342 | return findFontFile(base14Name->toStr()); |
1343 | } |
1344 | |
1345 | #else |
1346 | |
1347 | FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle, const std::vector<std::string> &filesToIgnore) |
1348 | { |
1349 | error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForFamilyAndStyle not implemented for this platform" ); |
1350 | return {}; |
1351 | } |
1352 | |
1353 | UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate) |
1354 | { |
1355 | error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForUChar not implemented for this platform" ); |
1356 | return {}; |
1357 | } |
1358 | |
1359 | GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString * /*substituteFontName*/) |
1360 | { |
1361 | return findFontFile(base14Name->toStr()); |
1362 | } |
1363 | |
1364 | static struct |
1365 | { |
1366 | const char *name; |
1367 | const char *t1FileName; |
1368 | const char *ttFileName; |
1369 | } displayFontTab[] = { { "Courier" , "n022003l.pfb" , "cour.ttf" }, |
1370 | { "Courier-Bold" , "n022004l.pfb" , "courbd.ttf" }, |
1371 | { "Courier-BoldOblique" , "n022024l.pfb" , "courbi.ttf" }, |
1372 | { "Courier-Oblique" , "n022023l.pfb" , "couri.ttf" }, |
1373 | { "Helvetica" , "n019003l.pfb" , "arial.ttf" }, |
1374 | { "Helvetica-Bold" , "n019004l.pfb" , "arialbd.ttf" }, |
1375 | { "Helvetica-BoldOblique" , "n019024l.pfb" , "arialbi.ttf" }, |
1376 | { "Helvetica-Oblique" , "n019023l.pfb" , "ariali.ttf" }, |
1377 | { "Symbol" , "s050000l.pfb" , nullptr }, |
1378 | { "Times-Bold" , "n021004l.pfb" , "timesbd.ttf" }, |
1379 | { "Times-BoldItalic" , "n021024l.pfb" , "timesbi.ttf" }, |
1380 | { "Times-Italic" , "n021023l.pfb" , "timesi.ttf" }, |
1381 | { "Times-Roman" , "n021003l.pfb" , "times.ttf" }, |
1382 | { "ZapfDingbats" , "d050000l.pfb" , nullptr }, |
1383 | { nullptr, nullptr, nullptr } }; |
1384 | |
1385 | static const char *displayFontDirs[] = { "/usr/share/ghostscript/fonts" , "/usr/local/share/ghostscript/fonts" , "/usr/share/fonts/default/Type1" , "/usr/share/fonts/default/ghostscript" , "/usr/share/fonts/type1/gsfonts" , nullptr }; |
1386 | |
1387 | void GlobalParams::setupBaseFonts(const char *dir) |
1388 | { |
1389 | FILE *f; |
1390 | int i, j; |
1391 | |
1392 | for (i = 0; displayFontTab[i].name; ++i) { |
1393 | if (fontFiles.count(displayFontTab[i].name) > 0) { |
1394 | continue; |
1395 | } |
1396 | std::unique_ptr<GooString> fontName = std::make_unique<GooString>(displayFontTab[i].name); |
1397 | std::unique_ptr<GooString> fileName; |
1398 | if (dir) { |
1399 | fileName.reset(appendToPath(new GooString(dir), displayFontTab[i].t1FileName)); |
1400 | if ((f = openFile(fileName->c_str(), "rb" ))) { |
1401 | fclose(f); |
1402 | } else { |
1403 | fileName.reset(); |
1404 | } |
1405 | } |
1406 | for (j = 0; !fileName && displayFontDirs[j]; ++j) { |
1407 | fileName.reset(appendToPath(new GooString(displayFontDirs[j]), displayFontTab[i].t1FileName)); |
1408 | if ((f = openFile(fileName->c_str(), "rb" ))) { |
1409 | fclose(f); |
1410 | } else { |
1411 | fileName.reset(); |
1412 | } |
1413 | } |
1414 | if (!fileName) { |
1415 | error(errConfig, -1, "No display font for '{0:s}'" , displayFontTab[i].name); |
1416 | continue; |
1417 | } |
1418 | addFontFile(fontName->toStr(), fileName->toStr()); |
1419 | } |
1420 | } |
1421 | |
1422 | GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString * /*substituteFontName*/, const GooString * /*base14Name*/) |
1423 | { |
1424 | const SysFontInfo *fi; |
1425 | GooString *path; |
1426 | |
1427 | const std::optional<std::string> &fontName = font->getName(); |
1428 | if (!fontName) { |
1429 | return nullptr; |
1430 | } |
1431 | |
1432 | path = nullptr; |
1433 | globalParamsLocker(); |
1434 | if ((fi = sysFonts->find(*fontName, font->isFixedWidth(), false))) { |
1435 | path = fi->path->copy(); |
1436 | *type = fi->type; |
1437 | *fontNum = fi->fontNum; |
1438 | } |
1439 | |
1440 | return path; |
1441 | } |
1442 | #endif |
1443 | |
1444 | std::string GlobalParams::getTextEncodingName() const |
1445 | { |
1446 | globalParamsLocker(); |
1447 | return textEncoding->toStr(); |
1448 | } |
1449 | |
1450 | const UnicodeMap *GlobalParams::getUtf8Map() |
1451 | { |
1452 | if (!utf8Map) { |
1453 | utf8Map = globalParams->getUnicodeMap(encodingName: "UTF-8" ); |
1454 | } |
1455 | |
1456 | return utf8Map; |
1457 | } |
1458 | |
1459 | bool GlobalParams::getPrintCommands() |
1460 | { |
1461 | globalParamsLocker(); |
1462 | return printCommands; |
1463 | } |
1464 | |
1465 | bool GlobalParams::getProfileCommands() |
1466 | { |
1467 | globalParamsLocker(); |
1468 | return profileCommands; |
1469 | } |
1470 | |
1471 | bool GlobalParams::getErrQuiet() |
1472 | { |
1473 | // no locking -- this function may get called from inside a locked |
1474 | // section |
1475 | return errQuiet; |
1476 | } |
1477 | |
1478 | CharCodeToUnicode *GlobalParams::getCIDToUnicode(const GooString *collection) |
1479 | { |
1480 | CharCodeToUnicode *ctu; |
1481 | |
1482 | globalParamsLocker(); |
1483 | if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(tag: collection))) { |
1484 | const auto cidToUnicode = cidToUnicodes.find(x: collection->toStr()); |
1485 | if (cidToUnicode != cidToUnicodes.end()) { |
1486 | if ((ctu = CharCodeToUnicode::parseCIDToUnicode(fileName: cidToUnicode->second.c_str(), collection))) { |
1487 | cidToUnicodeCache->add(ctu); |
1488 | } |
1489 | } |
1490 | } |
1491 | |
1492 | return ctu; |
1493 | } |
1494 | |
1495 | const UnicodeMap *GlobalParams::getUnicodeMap(const std::string &encodingName) |
1496 | { |
1497 | const UnicodeMap *map; |
1498 | |
1499 | if (!(map = getResidentUnicodeMap(encodingName))) { |
1500 | unicodeMapCacheLocker(); |
1501 | map = unicodeMapCache->getUnicodeMap(encodingName); |
1502 | } |
1503 | |
1504 | return map; |
1505 | } |
1506 | |
1507 | std::shared_ptr<CMap> GlobalParams::getCMap(const GooString *collection, const GooString *cMapName) |
1508 | { |
1509 | cMapCacheLocker(); |
1510 | return cMapCache->getCMap(collection, cMapName); |
1511 | } |
1512 | |
1513 | const UnicodeMap *GlobalParams::getTextEncoding() |
1514 | { |
1515 | return getUnicodeMap(encodingName: textEncoding->toStr()); |
1516 | } |
1517 | |
1518 | std::vector<std::string> GlobalParams::getEncodingNames() |
1519 | { |
1520 | std::vector<std::string> result; |
1521 | result.reserve(n: residentUnicodeMaps.size() + unicodeMaps.size()); |
1522 | for (const auto &unicodeMap : residentUnicodeMaps) { |
1523 | result.push_back(x: unicodeMap.first); |
1524 | } |
1525 | for (const auto &unicodeMap : unicodeMaps) { |
1526 | result.push_back(x: unicodeMap.first); |
1527 | } |
1528 | return result; |
1529 | } |
1530 | |
1531 | //------------------------------------------------------------------------ |
1532 | // functions to set parameters |
1533 | //------------------------------------------------------------------------ |
1534 | |
1535 | void GlobalParams::addFontFile(const std::string &fontName, const std::string &path) |
1536 | { |
1537 | globalParamsLocker(); |
1538 | fontFiles[fontName] = path; |
1539 | } |
1540 | |
1541 | void GlobalParams::setTextEncoding(const char *encodingName) |
1542 | { |
1543 | globalParamsLocker(); |
1544 | delete textEncoding; |
1545 | textEncoding = new GooString(encodingName); |
1546 | } |
1547 | |
1548 | void GlobalParams::setPrintCommands(bool printCommandsA) |
1549 | { |
1550 | globalParamsLocker(); |
1551 | printCommands = printCommandsA; |
1552 | } |
1553 | |
1554 | void GlobalParams::setProfileCommands(bool profileCommandsA) |
1555 | { |
1556 | globalParamsLocker(); |
1557 | profileCommands = profileCommandsA; |
1558 | } |
1559 | |
1560 | void GlobalParams::setErrQuiet(bool errQuietA) |
1561 | { |
1562 | globalParamsLocker(); |
1563 | errQuiet = errQuietA; |
1564 | } |
1565 | |
1566 | #ifdef ANDROID |
1567 | void GlobalParams::setFontDir(const std::string &fontDir) |
1568 | { |
1569 | # if defined(WITH_FONTCONFIGURATION_ANDROID) |
1570 | displayFontDir = fontDir; |
1571 | # endif |
1572 | } |
1573 | #endif |
1574 | |
1575 | GlobalParamsIniter::GlobalParamsIniter(ErrorCallback errorCallback) |
1576 | { |
1577 | const std::scoped_lock lock { mutex }; |
1578 | |
1579 | if (count == 0) { |
1580 | globalParams = std::make_unique<GlobalParams>(args: !customDataDir.empty() ? customDataDir.c_str() : nullptr); |
1581 | |
1582 | setErrorCallback(errorCallback); |
1583 | } |
1584 | |
1585 | count++; |
1586 | } |
1587 | |
1588 | GlobalParamsIniter::~GlobalParamsIniter() |
1589 | { |
1590 | const std::scoped_lock lock { mutex }; |
1591 | |
1592 | --count; |
1593 | |
1594 | if (count == 0) { |
1595 | globalParams.reset(); |
1596 | } |
1597 | } |
1598 | |
1599 | bool GlobalParamsIniter::setCustomDataDir(const std::string &dir) |
1600 | { |
1601 | const std::scoped_lock lock { mutex }; |
1602 | |
1603 | if (count == 0) { |
1604 | customDataDir = dir; |
1605 | return true; |
1606 | } |
1607 | |
1608 | return false; |
1609 | } |
1610 | |
1611 | std::mutex GlobalParamsIniter::mutex; |
1612 | int GlobalParamsIniter::count = 0; |
1613 | std::string GlobalParamsIniter::customDataDir; |
1614 | |