1 /* Written by Krzysztof Kowalczyk (http://blog.kowalczyk.info)
2    but mostly based on xpdf code.
3 
4    // Copyright (C) 2010, 2012 Hib Eris <hib@hiberis.nl>
5    // Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
6    // Copyright (C) 2012 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
7    // Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
8    // Copyright (C) 2012 Mark Brand <mabrand@mabrand.nl>
9    // Copyright (C) 2013, 2018, 2019 Adam Reichold <adamreichold@myopera.com>
10    // Copyright (C) 2013 Dmytro Morgun <lztoad@gmail.com>
11    // Copyright (C) 2017 Christoph Cullmann <cullmann@kde.org>
12    // Copyright (C) 2017, 2018, 2020, 2021 Albert Astals Cid <aacid@kde.org>
13    // 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
14    // Copyright (C) 2019 Christian Persch <chpe@src.gnome.org>
15    // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de>
16 
17 TODO: instead of a fixed mapping defined in displayFontTab, it could
18 scan the whole fonts directory, parse TTF files and build font
19 description for all fonts available in Windows. That's how MuPDF works.
20 */
21 
22 #ifndef PACKAGE_NAME
23 #    include <config.h>
24 #endif
25 
26 #include <windows.h>
27 #if !(_WIN32_IE >= 0x0500)
28 #    error "_WIN32_IE must be defined >= 0x0500 for SHGFP_TYPE_CURRENT from shlobj.h"
29 #endif
30 #include <shlobj.h>
31 #include <cstring>
32 #include <cstdio>
33 #include <cctype>
34 #include <cassert>
35 
36 #include "goo/gmem.h"
37 #include "goo/GooString.h"
38 #include "goo/gfile.h"
39 #include "Error.h"
40 #include "NameToCharCode.h"
41 #include "CharCodeToUnicode.h"
42 #include "UnicodeMap.h"
43 #include "CMap.h"
44 #include "FontEncodingTables.h"
45 #include "GlobalParams.h"
46 #include "GfxFont.h"
47 #include <sys/stat.h>
48 #include "Object.h"
49 #include "Stream.h"
50 #include "Lexer.h"
51 #include "Parser.h"
52 
53 #define DEFAULT_SUBSTITUTE_FONT "Helvetica"
54 #define DEFAULT_CID_FONT_AC1_MSWIN "MingLiU" /* Adobe-CNS1 for Taiwan, HongKong */
55 #define DEFAULT_CID_FONT_AG1_MSWIN "SimSun" /* Adobe-GB1 for PRC, Singapore */
56 #define DEFAULT_CID_FONT_AJ1_MSWIN "MS-Mincho" /* Adobe-Japan1 */
57 #define DEFAULT_CID_FONT_AJ2_MSWIN "MS-Mincho" /* Adobe-Japan2 (legacy) */
58 #define DEFAULT_CID_FONT_AK1_MSWIN "Batang" /* Adobe-Korea1 */
59 #define DEFAULT_CID_FONT_MSWIN "ArialUnicode" /* Unknown */
60 
61 static const struct
62 {
63     const char *name;
64     const char *t1FileName;
65     const char *ttFileName;
66     bool warnIfMissing;
67 } displayFontTab[] = { { "Courier", "n022003l.pfb", "cour.ttf", true },
68                        { "Courier-Bold", "n022004l.pfb", "courbd.ttf", true },
69                        { "Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", true },
70                        { "Courier-Oblique", "n022023l.pfb", "couri.ttf", true },
71                        { "Helvetica", "n019003l.pfb", "arial.ttf", true },
72                        { "Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", true },
73                        { "Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", true },
74                        { "Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", true },
75                        // TODO: not sure if "symbol.ttf" is right
76                        { "Symbol", "s050000l.pfb", "symbol.ttf", true },
77                        { "Times-Bold", "n021004l.pfb", "timesbd.ttf", true },
78                        { "Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", true },
79                        { "Times-Italic", "n021023l.pfb", "timesi.ttf", true },
80                        { "Times-Roman", "n021003l.pfb", "times.ttf", true },
81                        // TODO: not sure if "wingding.ttf" is right
82                        { "ZapfDingbats", "d050000l.pfb", "wingding.ttf", true },
83 
84                        // those seem to be frequently accessed by PDF files and I kind of guess
85                        // which font file do the refer to
86                        { "Palatino", nullptr, "pala.ttf", true },
87                        { "Palatino-Roman", nullptr, "pala.ttf", true },
88                        { "Palatino-Bold", nullptr, "palab.ttf", true },
89                        { "Palatino-Italic", nullptr, "palai.ttf", true },
90                        { "Palatino,Italic", nullptr, "palai.ttf", true },
91                        { "Palatino-BoldItalic", nullptr, "palabi.ttf", true },
92 
93                        { "ArialBlack", nullptr, "arialbd.ttf", true },
94 
95                        { "ArialNarrow", nullptr, "arialn.ttf", true },
96                        { "ArialNarrow,Bold", nullptr, "arialnb.ttf", true },
97                        { "ArialNarrow,Italic", nullptr, "arialni.ttf", true },
98                        { "ArialNarrow,BoldItalic", nullptr, "arialnbi.ttf", true },
99                        { "ArialNarrow-Bold", nullptr, "arialnb.ttf", true },
100                        { "ArialNarrow-Italic", nullptr, "arialni.ttf", true },
101                        { "ArialNarrow-BoldItalic", nullptr, "arialnbi.ttf", true },
102 
103                        { "HelveticaNarrow", nullptr, "arialn.ttf", true },
104                        { "HelveticaNarrow,Bold", nullptr, "arialnb.ttf", true },
105                        { "HelveticaNarrow,Italic", nullptr, "arialni.ttf", true },
106                        { "HelveticaNarrow,BoldItalic", nullptr, "arialnbi.ttf", true },
107                        { "HelveticaNarrow-Bold", nullptr, "arialnb.ttf", true },
108                        { "HelveticaNarrow-Italic", nullptr, "arialni.ttf", true },
109                        { "HelveticaNarrow-BoldItalic", nullptr, "arialnbi.ttf", true },
110 
111                        { "BookAntiqua", nullptr, "bkant.ttf", true },
112                        { "BookAntiqua,Bold", nullptr, "bkant.ttf", true },
113                        { "BookAntiqua,Italic", nullptr, "bkant.ttf", true },
114                        { "BookAntiqua,BoldItalic", nullptr, "bkant.ttf", true },
115                        { "BookAntiqua-Bold", nullptr, "bkant.ttf", true },
116                        { "BookAntiqua-Italic", nullptr, "bkant.ttf", true },
117                        { "BookAntiqua-BoldItalic", nullptr, "bkant.ttf", true },
118 
119                        { "Verdana", nullptr, "verdana.ttf", true },
120                        { "Verdana,Bold", nullptr, "verdanab.ttf", true },
121                        { "Verdana,Italic", nullptr, "verdanai.ttf", true },
122                        { "Verdana,BoldItalic", nullptr, "verdanaz.ttf", true },
123                        { "Verdana-Bold", nullptr, "verdanab.ttf", true },
124                        { "Verdana-Italic", nullptr, "verdanai.ttf", true },
125                        { "Verdana-BoldItalic", nullptr, "verdanaz.ttf", true },
126 
127                        { "Tahoma", nullptr, "tahoma.ttf", true },
128                        { "Tahoma,Bold", nullptr, "tahomabd.ttf", true },
129                        { "Tahoma,Italic", nullptr, "tahoma.ttf", true },
130                        { "Tahoma,BoldItalic", nullptr, "tahomabd.ttf", true },
131                        { "Tahoma-Bold", nullptr, "tahomabd.ttf", true },
132                        { "Tahoma-Italic", nullptr, "tahoma.ttf", true },
133                        { "Tahoma-BoldItalic", nullptr, "tahomabd.ttf", true },
134 
135                        { "CCRIKH+Verdana", nullptr, "verdana.ttf", true },
136                        { "CCRIKH+Verdana,Bold", nullptr, "verdanab.ttf", true },
137                        { "CCRIKH+Verdana,Italic", nullptr, "verdanai.ttf", true },
138                        { "CCRIKH+Verdana,BoldItalic", nullptr, "verdanaz.ttf", true },
139                        { "CCRIKH+Verdana-Bold", nullptr, "verdanab.ttf", true },
140                        { "CCRIKH+Verdana-Italic", nullptr, "verdanai.ttf", true },
141                        { "CCRIKH+Verdana-BoldItalic", nullptr, "verdanaz.ttf", true },
142 
143                        { "Georgia", nullptr, "georgia.ttf", true },
144                        { "Georgia,Bold", nullptr, "georgiab.ttf", true },
145                        { "Georgia,Italic", nullptr, "georgiai.ttf", true },
146                        { "Georgia,BoldItalic", nullptr, "georgiaz.ttf", true },
147                        { "Georgia-Bold", nullptr, "georgiab.ttf", true },
148                        { "Georgia-Italic", nullptr, "georgiai.ttf", true },
149                        { "Georgia-BoldItalic", nullptr, "georgiaz.ttf", true },
150 
151                        // fallback for Adobe CID fonts:
152                        { "MingLiU", nullptr, "mingliu.ttf", false },
153                        { "SimSun", nullptr, "simsun.ttf", false },
154                        { "MS-Mincho", nullptr, "msmincho.ttf", false },
155                        { "Batang", nullptr, "batang.ttf", false },
156                        { "ArialUnicode", nullptr, "arialuni.ttf", true },
157                        {} };
158 
159 #define FONTS_SUBDIR "\\fonts"
160 
GetWindowsFontDir(char * winFontDir,int cbWinFontDirLen)161 static void GetWindowsFontDir(char *winFontDir, int cbWinFontDirLen)
162 {
163     BOOL(__stdcall * SHGetSpecialFolderPathFunc)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
164     HRESULT(__stdcall * SHGetFolderPathFunc)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPSTR pszPath);
165 
166     // SHGetSpecialFolderPath isn't available in older versions of shell32.dll (Win95 and
167     // WinNT4), so do a dynamic load of ANSI versions.
168     winFontDir[0] = '\0';
169 
170     HMODULE hLib = LoadLibraryA("shell32.dll");
171     if (hLib) {
172         SHGetFolderPathFunc = (HRESULT(__stdcall *)(HWND, int, HANDLE, DWORD, LPSTR))GetProcAddress(hLib, "SHGetFolderPathA");
173         if (SHGetFolderPathFunc)
174             (*SHGetFolderPathFunc)(nullptr, CSIDL_FONTS, nullptr, SHGFP_TYPE_CURRENT, winFontDir);
175 
176         if (!winFontDir[0]) {
177             // Try an older function
178             SHGetSpecialFolderPathFunc = (BOOL(__stdcall *)(HWND, LPSTR, int, BOOL))GetProcAddress(hLib, "SHGetSpecialFolderPathA");
179             if (SHGetSpecialFolderPathFunc)
180                 (*SHGetSpecialFolderPathFunc)(nullptr, winFontDir, CSIDL_FONTS, FALSE);
181         }
182         FreeLibrary(hLib);
183     }
184     if (winFontDir[0])
185         return;
186 
187     // Try older DLL
188     hLib = LoadLibraryA("SHFolder.dll");
189     if (hLib) {
190         SHGetFolderPathFunc = (HRESULT(__stdcall *)(HWND, int, HANDLE, DWORD, LPSTR))GetProcAddress(hLib, "SHGetFolderPathA");
191         if (SHGetFolderPathFunc)
192             (*SHGetFolderPathFunc)(nullptr, CSIDL_FONTS, nullptr, SHGFP_TYPE_CURRENT, winFontDir);
193         FreeLibrary(hLib);
194     }
195     if (winFontDir[0])
196         return;
197 
198     // Everything else failed so the standard fonts directory.
199     GetWindowsDirectoryA(winFontDir, cbWinFontDirLen);
200     if (winFontDir[0]) {
201         strncat(winFontDir, FONTS_SUBDIR, cbWinFontDirLen);
202         winFontDir[cbWinFontDirLen - 1] = 0;
203     }
204 }
205 
FileExists(const char * path)206 static bool FileExists(const char *path)
207 {
208     FILE *f = openFile(path, "rb");
209     if (f) {
210         fclose(f);
211         return true;
212     }
213     return false;
214 }
215 
scanWindowsFonts(GooString * winFontDir)216 void SysFontList::scanWindowsFonts(GooString *winFontDir)
217 {
218     OSVERSIONINFO version;
219     const char *path;
220     DWORD idx, valNameLen, dataLen, type;
221     HKEY regKey;
222     char valName[1024], data[1024];
223     int n, fontNum;
224     char *p0, *p1;
225     GooString *fontPath;
226 
227     version.dwOSVersionInfoSize = sizeof(version);
228     GetVersionEx(&version);
229     if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
230         path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\";
231     } else {
232         path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
233     }
234     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &regKey) == ERROR_SUCCESS) {
235         idx = 0;
236         while (1) {
237             valNameLen = sizeof(valName) - 1;
238             dataLen = sizeof(data) - 1;
239             if (RegEnumValueA(regKey, idx, valName, &valNameLen, nullptr, &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) {
240                 break;
241             }
242             if (type == REG_SZ && valNameLen > 0 && valNameLen < sizeof(valName) && dataLen > 0 && dataLen < sizeof(data)) {
243                 valName[valNameLen] = '\0';
244                 data[dataLen] = '\0';
245                 n = strlen(data);
246                 if (!strcasecmp(data + n - 4, ".ttf") || !strcasecmp(data + n - 4, ".ttc") || !strcasecmp(data + n - 4, ".otf")) {
247                     fontPath = new GooString(data);
248                     if (!(dataLen >= 3 && data[1] == ':' && data[2] == '\\')) {
249                         fontPath->insert(0, '\\');
250                         fontPath->insert(0, winFontDir);
251                         fontPath->append('\0');
252                     }
253                     p0 = valName;
254                     fontNum = 0;
255                     while (*p0) {
256                         p1 = strstr(p0, " & ");
257                         if (p1) {
258                             *p1 = '\0';
259                             p1 = p1 + 3;
260                         } else {
261                             p1 = p0 + strlen(p0);
262                         }
263                         fonts.push_back(makeWindowsFont(p0, fontNum, fontPath->c_str()));
264                         p0 = p1;
265                         ++fontNum;
266                     }
267                     delete fontPath;
268                 }
269             }
270             ++idx;
271         }
272         RegCloseKey(regKey);
273     }
274 }
275 
makeWindowsFont(const char * name,int fontNum,const char * path)276 SysFontInfo *SysFontList::makeWindowsFont(const char *name, int fontNum, const char *path)
277 {
278     int n;
279     bool bold, italic, oblique, fixedWidth;
280     GooString *s;
281     char c;
282     int i;
283     SysFontType type;
284     GooString substituteName;
285 
286     n = strlen(name);
287     bold = italic = oblique = fixedWidth = false;
288 
289     // remove trailing ' (TrueType)'
290     if (n > 11 && !strncmp(name + n - 11, " (TrueType)", 11)) {
291         n -= 11;
292     }
293 
294     // remove trailing ' (OpenType)'
295     if (n > 11 && !strncmp(name + n - 11, " (OpenType)", 11)) {
296         n -= 11;
297     }
298 
299     // remove trailing ' Italic'
300     if (n > 7 && !strncmp(name + n - 7, " Italic", 7)) {
301         n -= 7;
302         italic = true;
303     }
304 
305     // remove trailing ' Oblique'
306     if (n > 7 && !strncmp(name + n - 8, " Oblique", 8)) {
307         n -= 8;
308         oblique = true;
309     }
310 
311     // remove trailing ' Bold'
312     if (n > 5 && !strncmp(name + n - 5, " Bold", 5)) {
313         n -= 5;
314         bold = true;
315     }
316 
317     // remove trailing ' Regular'
318     if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) {
319         n -= 8;
320     }
321 
322     // the familyname cannot indicate whether a font is fixedWidth or not.
323     // some well-known fixedWidth typeface family names or keyword are checked.
324     if (strstr(name, "Courier") || strstr(name, "Fixed") || (strstr(name, "Mono") && !strstr(name, "Monotype")) || strstr(name, "Typewriter"))
325         fixedWidth = true;
326     else
327         fixedWidth = false;
328 
329     //----- normalize the font name
330     s = new GooString(name, n);
331     i = 0;
332     while (i < s->getLength()) {
333         c = s->getChar(i);
334         if (c == ' ' || c == ',' || c == '-') {
335             s->del(i);
336         } else {
337             ++i;
338         }
339     }
340 
341     if (!strcasecmp(path + strlen(path) - 4, ".ttc")) {
342         type = sysFontTTC;
343     } else {
344         type = sysFontTTF;
345     }
346 
347     return new SysFontInfo(s, bold, italic, oblique, fixedWidth, new GooString(path), type, fontNum, substituteName.copy());
348 }
349 
replaceSuffix(GooString * path,const char * suffixA,const char * suffixB)350 static GooString *replaceSuffix(GooString *path, const char *suffixA, const char *suffixB)
351 {
352     int suffLenA = strlen(suffixA);
353     int suffLenB = strlen(suffixB);
354     int baseLenA = path->getLength() - suffLenA;
355     int baseLenB = path->getLength() - suffLenB;
356 
357     if (!strcasecmp(path->c_str() + baseLenA, suffixA)) {
358         path->del(baseLenA, suffLenA)->append(suffixB);
359     } else if (!strcasecmp(path->c_str() + baseLenB, suffixB)) {
360         path->del(baseLenB, suffLenB)->append(suffixA);
361     }
362 
363     return path;
364 }
365 
setupBaseFonts(const char * dir)366 void GlobalParams::setupBaseFonts(const char *dir)
367 {
368     const char *dataRoot = popplerDataDir ? popplerDataDir : POPPLER_DATADIR;
369     GooString *fileName = nullptr;
370     GooFile *file;
371 
372     if (baseFontsInitialized)
373         return;
374     baseFontsInitialized = true;
375 
376     char winFontDir[MAX_PATH];
377     GetWindowsFontDir(winFontDir, sizeof(winFontDir));
378 
379     for (int i = 0; displayFontTab[i].name; ++i) {
380         if (fontFiles.count(displayFontTab[i].name) > 0)
381             continue;
382 
383         GooString *fontName = new GooString(displayFontTab[i].name);
384 
385         if (dir) {
386             GooString *fontPath = appendToPath(new GooString(dir), displayFontTab[i].t1FileName);
387             if (FileExists(fontPath->c_str()) || FileExists(replaceSuffix(fontPath, ".pfb", ".pfa")->c_str())) {
388                 addFontFile(fontName, fontPath);
389                 continue;
390             }
391             delete fontPath;
392         }
393 
394         if (winFontDir[0] && displayFontTab[i].ttFileName) {
395             GooString *fontPath = appendToPath(new GooString(winFontDir), displayFontTab[i].ttFileName);
396             if (FileExists(fontPath->c_str()) || FileExists(replaceSuffix(fontPath, ".ttc", ".ttf")->c_str())) {
397                 addFontFile(fontName, fontPath);
398                 continue;
399             }
400             delete fontPath;
401         }
402 
403         if (displayFontTab[i].warnIfMissing) {
404             error(errSyntaxError, -1, "No display font for '{0:s}'", displayFontTab[i].name);
405             delete fontName;
406         }
407     }
408     if (winFontDir[0]) {
409         GooString gooWinFontsDir(winFontDir);
410         sysFonts->scanWindowsFonts(&gooWinFontsDir);
411     }
412 
413     fileName = new GooString(dataRoot);
414     fileName->append("/cidfmap");
415 
416     // try to open file
417     file = GooFile::open(fileName->toStr());
418 
419     if (file != nullptr) {
420         Parser *parser;
421         parser = new Parser(nullptr, new FileStream(file, 0, false, file->size(), Object(objNull)), true);
422         Object obj1 = parser->getObj();
423         while (!obj1.isEOF()) {
424             Object obj2 = parser->getObj();
425             if (obj1.isName()) {
426                 // Substitutions
427                 if (obj2.isDict()) {
428                     Object obj3 = obj2.getDict()->lookup("Path");
429                     if (obj3.isString())
430                         addFontFile(new GooString(obj1.getName()), obj3.getString()->copy());
431                     // Aliases
432                 } else if (obj2.isName()) {
433                     substFiles.emplace(obj1.getName(), obj2.getName());
434                 }
435             }
436             obj1 = parser->getObj();
437             // skip trailing ';'
438             while (obj1.isCmd(";")) {
439                 obj1 = parser->getObj();
440             }
441         }
442         delete file;
443         delete parser;
444     } else {
445         delete fileName;
446     }
447 }
448 
findSubstituteName(const GfxFont * font,const std::unordered_map<std::string,std::string> & fontFiles,const std::unordered_map<std::string,std::string> & substFiles,const char * origName)449 static const char *findSubstituteName(const GfxFont *font, const std::unordered_map<std::string, std::string> &fontFiles, const std::unordered_map<std::string, std::string> &substFiles, const char *origName)
450 {
451     assert(origName);
452     if (!origName)
453         return nullptr;
454     GooString *name2 = new GooString(origName);
455     int n = strlen(origName);
456     // remove trailing "-Identity-H"
457     if (n > 11 && !strcmp(name2->c_str() + n - 11, "-Identity-H")) {
458         name2->del(n - 11, 11);
459         n -= 11;
460     }
461     // remove trailing "-Identity-V"
462     if (n > 11 && !strcmp(name2->c_str() + n - 11, "-Identity-V")) {
463         name2->del(n - 11, 11);
464         n -= 11;
465     }
466     const auto substFile = substFiles.find(name2->c_str());
467     if (substFile != substFiles.end()) {
468         delete name2;
469         return substFile->second.c_str();
470     }
471 
472     /* TODO: try to at least guess bold/italic/bolditalic from the name */
473     delete name2;
474     if (font->isCIDFont()) {
475         const GooString *collection = ((GfxCIDFont *)font)->getCollection();
476 
477         const char *name3 = nullptr;
478         if (!collection->cmp("Adobe-CNS1"))
479             name3 = DEFAULT_CID_FONT_AC1_MSWIN;
480         else if (!collection->cmp("Adobe-GB1"))
481             name3 = DEFAULT_CID_FONT_AG1_MSWIN;
482         else if (!collection->cmp("Adobe-Japan1"))
483             name3 = DEFAULT_CID_FONT_AJ1_MSWIN;
484         else if (!collection->cmp("Adobe-Japan2"))
485             name3 = DEFAULT_CID_FONT_AJ2_MSWIN;
486         else if (!collection->cmp("Adobe-Korea1"))
487             name3 = DEFAULT_CID_FONT_AK1_MSWIN;
488 
489         if (name3 && fontFiles.count(name3) != 0)
490             return name3;
491 
492         if (fontFiles.count(DEFAULT_CID_FONT_MSWIN) != 0)
493             return DEFAULT_CID_FONT_MSWIN;
494     }
495     return DEFAULT_SUBSTITUTE_FONT;
496 }
497 
498 /* Windows implementation of external font matching code */
findSystemFontFile(const GfxFont * font,SysFontType * type,int * fontNum,GooString * substituteFontName,const GooString * base14Name)499 GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name)
500 {
501     const SysFontInfo *fi;
502     GooString *path = nullptr;
503     const GooString *fontName = font->getName();
504     if (!fontName)
505         return nullptr;
506     std::unique_lock<std::recursive_mutex> locker(mutex);
507     setupBaseFonts(nullptr);
508 
509     // TODO: base14Name should be changed?
510     // In the system using FontConfig, findSystemFontFile() uses
511     // base14Name only for the creation of query pattern.
512 
513     if ((fi = sysFonts->find(fontName, false, false))) {
514         path = fi->path->copy();
515         *type = fi->type;
516         *fontNum = fi->fontNum;
517         if (substituteFontName)
518             substituteFontName->Set(fi->substituteName->c_str());
519     } else {
520         GooString *substFontName = new GooString(findSubstituteName(font, fontFiles, substFiles, fontName->c_str()));
521         error(errSyntaxError, -1, "Couldn't find a font for '{0:t}', subst is '{1:t}'", fontName, substFontName);
522         const auto fontFile = fontFiles.find(substFontName->toStr());
523         if (fontFile != fontFiles.end()) {
524             path = new GooString(fontFile->second.c_str());
525             if (substituteFontName)
526                 substituteFontName->Set(path->c_str());
527             if (!strcasecmp(path->c_str() + path->getLength() - 4, ".ttc")) {
528                 *type = sysFontTTC;
529             } else {
530                 *type = sysFontTTF;
531             }
532             *fontNum = 0;
533         }
534     }
535 
536     return path;
537 }
538