1 /* Written by Krzysztof Kowalczyk (http://blog.kowalczyk.info)
2 but mostly based on xpdf code.
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 Adrian Johnson <ajohnson@redneon.com>
8 // Copyright (C) 2012 Mark Brand <mabrand@mabrand.nl>
9 // Copyright (C) 2013 Adam Reichold <adamreichold@myopera.com>
10 // Copyright (C) 2013 Dmytro Morgun <lztoad@gmail.com>
12 TODO: instead of a fixed mapping defined in displayFontTab, it could
13 scan the whole fonts directory, parse TTF files and build font
14 description for all fonts available in Windows. That's how MuPDF works.
21 #ifdef USE_GCC_PRAGMAS
22 #pragma implementation
26 #if !(_WIN32_IE >= 0x0500)
27 #error "_WIN32_IE must be defined >= 0x0500 for SHGFP_TYPE_CURRENT from shlobj.h"
36 #include "goo/GooString.h"
37 #include "goo/GooList.h"
38 #include "goo/GooHash.h"
39 #include "goo/gfile.h"
41 #include "NameToCharCode.h"
42 #include "CharCodeToUnicode.h"
43 #include "UnicodeMap.h"
45 #include "BuiltinFontTables.h"
46 #include "FontEncodingTables.h"
47 #include "GlobalParams.h"
56 # define lockGlobalParams gLockMutex(&mutex)
57 # define lockUnicodeMapCache gLockMutex(&unicodeMapCacheMutex)
58 # define lockCMapCache gLockMutex(&cMapCacheMutex)
59 # define unlockGlobalParams gUnlockMutex(&mutex)
60 # define unlockUnicodeMapCache gUnlockMutex(&unicodeMapCacheMutex)
61 # define unlockCMapCache gUnlockMutex(&cMapCacheMutex)
63 # define lockGlobalParams
64 # define lockUnicodeMapCache
65 # define lockCMapCache
66 # define unlockGlobalParams
67 # define unlockUnicodeMapCache
68 # define unlockCMapCache
71 #define DEFAULT_SUBSTITUTE_FONT "Helvetica"
72 #define DEFAULT_CID_FONT_AC1_MSWIN "MingLiU" /* Adobe-CNS1 for Taiwan, HongKong */
73 #define DEFAULT_CID_FONT_AG1_MSWIN "SimSun" /* Adobe-GB1 for PRC, Singapore */
74 #define DEFAULT_CID_FONT_AJ1_MSWIN "MS-Mincho" /* Adobe-Japan1 */
75 #define DEFAULT_CID_FONT_AJ2_MSWIN "MS-Mincho" /* Adobe-Japan2 (legacy) */
76 #define DEFAULT_CID_FONT_AK1_MSWIN "Batang" /* Adobe-Korea1 */
77 #define DEFAULT_CID_FONT_MSWIN "ArialUnicode" /* Unknown */
81 const char *t1FileName
;
82 const char *ttFileName
;
84 } displayFontTab
[] = {
85 {"Courier", "n022003l.pfb", "cour.ttf", gTrue
},
86 {"Courier-Bold", "n022004l.pfb", "courbd.ttf", gTrue
},
87 {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", gTrue
},
88 {"Courier-Oblique", "n022023l.pfb", "couri.ttf", gTrue
},
89 {"Helvetica", "n019003l.pfb", "arial.ttf", gTrue
},
90 {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", gTrue
},
91 {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", gTrue
},
92 {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", gTrue
},
93 // TODO: not sure if "symbol.ttf" is right
94 {"Symbol", "s050000l.pfb", "symbol.ttf", gTrue
},
95 {"Times-Bold", "n021004l.pfb", "timesbd.ttf", gTrue
},
96 {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", gTrue
},
97 {"Times-Italic", "n021023l.pfb", "timesi.ttf", gTrue
},
98 {"Times-Roman", "n021003l.pfb", "times.ttf", gTrue
},
99 // TODO: not sure if "wingding.ttf" is right
100 {"ZapfDingbats", "d050000l.pfb", "wingding.ttf", gTrue
},
102 // those seem to be frequently accessed by PDF files and I kind of guess
103 // which font file do the refer to
104 {"Palatino", NULL
, "pala.ttf", gTrue
},
105 {"Palatino-Roman", NULL
, "pala.ttf", gTrue
},
106 {"Palatino-Bold", NULL
, "palab.ttf", gTrue
},
107 {"Palatino-Italic", NULL
, "palai.ttf", gTrue
},
108 {"Palatino,Italic", NULL
, "palai.ttf", gTrue
},
109 {"Palatino-BoldItalic", NULL
, "palabi.ttf", gTrue
},
111 {"ArialBlack", NULL
, "arialbd.ttf", gTrue
},
113 {"ArialNarrow", NULL
, "arialn.ttf", gTrue
},
114 {"ArialNarrow,Bold", NULL
, "arialnb.ttf", gTrue
},
115 {"ArialNarrow,Italic", NULL
, "arialni.ttf", gTrue
},
116 {"ArialNarrow,BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
117 {"ArialNarrow-Bold", NULL
, "arialnb.ttf", gTrue
},
118 {"ArialNarrow-Italic", NULL
, "arialni.ttf", gTrue
},
119 {"ArialNarrow-BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
121 {"HelveticaNarrow", NULL
, "arialn.ttf", gTrue
},
122 {"HelveticaNarrow,Bold", NULL
, "arialnb.ttf", gTrue
},
123 {"HelveticaNarrow,Italic", NULL
, "arialni.ttf", gTrue
},
124 {"HelveticaNarrow,BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
125 {"HelveticaNarrow-Bold", NULL
, "arialnb.ttf", gTrue
},
126 {"HelveticaNarrow-Italic", NULL
, "arialni.ttf", gTrue
},
127 {"HelveticaNarrow-BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
129 {"BookAntiqua", NULL
, "bkant.ttf", gTrue
},
130 {"BookAntiqua,Bold", NULL
, "bkant.ttf", gTrue
},
131 {"BookAntiqua,Italic", NULL
, "bkant.ttf", gTrue
},
132 {"BookAntiqua,BoldItalic", NULL
, "bkant.ttf", gTrue
},
133 {"BookAntiqua-Bold", NULL
, "bkant.ttf", gTrue
},
134 {"BookAntiqua-Italic", NULL
, "bkant.ttf", gTrue
},
135 {"BookAntiqua-BoldItalic", NULL
, "bkant.ttf", gTrue
},
137 {"Verdana", NULL
, "verdana.ttf", gTrue
},
138 {"Verdana,Bold", NULL
, "verdanab.ttf", gTrue
},
139 {"Verdana,Italic", NULL
, "verdanai.ttf", gTrue
},
140 {"Verdana,BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
141 {"Verdana-Bold", NULL
, "verdanab.ttf", gTrue
},
142 {"Verdana-Italic", NULL
, "verdanai.ttf", gTrue
},
143 {"Verdana-BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
145 {"Tahoma", NULL
, "tahoma.ttf", gTrue
},
146 {"Tahoma,Bold", NULL
, "tahomabd.ttf", gTrue
},
147 {"Tahoma,Italic", NULL
, "tahoma.ttf", gTrue
},
148 {"Tahoma,BoldItalic", NULL
, "tahomabd.ttf", gTrue
},
149 {"Tahoma-Bold", NULL
, "tahomabd.ttf", gTrue
},
150 {"Tahoma-Italic", NULL
, "tahoma.ttf", gTrue
},
151 {"Tahoma-BoldItalic", NULL
, "tahomabd.ttf", gTrue
},
153 {"CCRIKH+Verdana", NULL
, "verdana.ttf", gTrue
},
154 {"CCRIKH+Verdana,Bold", NULL
, "verdanab.ttf", gTrue
},
155 {"CCRIKH+Verdana,Italic", NULL
, "verdanai.ttf", gTrue
},
156 {"CCRIKH+Verdana,BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
157 {"CCRIKH+Verdana-Bold", NULL
, "verdanab.ttf", gTrue
},
158 {"CCRIKH+Verdana-Italic", NULL
, "verdanai.ttf", gTrue
},
159 {"CCRIKH+Verdana-BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
161 {"Georgia", NULL
, "georgia.ttf", gTrue
},
162 {"Georgia,Bold", NULL
, "georgiab.ttf", gTrue
},
163 {"Georgia,Italic", NULL
, "georgiai.ttf", gTrue
},
164 {"Georgia,BoldItalic", NULL
, "georgiaz.ttf", gTrue
},
165 {"Georgia-Bold", NULL
, "georgiab.ttf", gTrue
},
166 {"Georgia-Italic", NULL
, "georgiai.ttf", gTrue
},
167 {"Georgia-BoldItalic", NULL
, "georgiaz.ttf", gTrue
},
169 // fallback for Adobe CID fonts:
170 {"MingLiU", NULL
, "mingliu.ttf", gFalse
},
171 {"SimSun", NULL
, "simsun.ttf", gFalse
},
172 {"MS-Mincho", NULL
, "msmincho.ttf", gFalse
},
173 {"Batang", NULL
, "batang.ttf", gFalse
},
174 {"ArialUnicode", NULL
, "arialuni.ttf", gTrue
},
178 #define FONTS_SUBDIR "\\fonts"
180 static void GetWindowsFontDir(char *winFontDir
, int cbWinFontDirLen
)
182 BOOL (__stdcall
*SHGetSpecialFolderPathFunc
)(HWND hwndOwner
,
186 HRESULT (__stdcall
*SHGetFolderPathFunc
)(HWND hwndOwner
,
192 // SHGetSpecialFolderPath isn't available in older versions of shell32.dll (Win95 and
193 // WinNT4), so do a dynamic load of ANSI versions.
194 winFontDir
[0] = '\0';
196 HMODULE hLib
= LoadLibrary("shell32.dll");
198 SHGetFolderPathFunc
= (HRESULT (__stdcall
*)(HWND
, int, HANDLE
, DWORD
, LPTSTR
))
199 GetProcAddress(hLib
, "SHGetFolderPathA");
200 if (SHGetFolderPathFunc
)
201 (*SHGetFolderPathFunc
)(NULL
, CSIDL_FONTS
, NULL
, SHGFP_TYPE_CURRENT
, winFontDir
);
203 if (!winFontDir
[0]) {
204 // Try an older function
205 SHGetSpecialFolderPathFunc
= (BOOL (__stdcall
*)(HWND
, LPTSTR
, int, BOOL
))
206 GetProcAddress(hLib
, "SHGetSpecialFolderPathA");
207 if (SHGetSpecialFolderPathFunc
)
208 (*SHGetSpecialFolderPathFunc
)(NULL
, winFontDir
, CSIDL_FONTS
, FALSE
);
216 hLib
= LoadLibrary("SHFolder.dll");
218 SHGetFolderPathFunc
= (HRESULT (__stdcall
*)(HWND
, int, HANDLE
, DWORD
, LPTSTR
))
219 GetProcAddress(hLib
, "SHGetFolderPathA");
220 if (SHGetFolderPathFunc
)
221 (*SHGetFolderPathFunc
)(NULL
, CSIDL_FONTS
, NULL
, SHGFP_TYPE_CURRENT
, winFontDir
);
227 // Everything else failed so the standard fonts directory.
228 GetWindowsDirectory(winFontDir
, cbWinFontDirLen
);
230 strncat(winFontDir
, FONTS_SUBDIR
, cbWinFontDirLen
);
231 winFontDir
[cbWinFontDirLen
-1] = 0;
235 static bool FileExists(const char *path
)
237 FILE * f
= fopen(path
, "rb");
245 void SysFontList::scanWindowsFonts(GooString
*winFontDir
) {
246 OSVERSIONINFO version
;
248 DWORD idx
, valNameLen
, dataLen
, type
;
250 char valName
[1024], data
[1024];
255 version
.dwOSVersionInfoSize
= sizeof(version
);
256 GetVersionEx(&version
);
257 if (version
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
258 path
= "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\";
260 path
= "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
262 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, path
, 0,
263 KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
,
264 ®Key
) == ERROR_SUCCESS
) {
267 valNameLen
= sizeof(valName
) - 1;
268 dataLen
= sizeof(data
) - 1;
269 if (RegEnumValue(regKey
, idx
, valName
, &valNameLen
, NULL
,
270 &type
, (LPBYTE
)data
, &dataLen
) != ERROR_SUCCESS
) {
273 if (type
== REG_SZ
&&
274 valNameLen
> 0 && valNameLen
< sizeof(valName
) &&
275 dataLen
> 0 && dataLen
< sizeof(data
)) {
276 valName
[valNameLen
] = '\0';
277 data
[dataLen
] = '\0';
279 if (!strcasecmp(data
+ n
- 4, ".ttf") ||
280 !strcasecmp(data
+ n
- 4, ".ttc") ||
281 !strcasecmp(data
+ n
- 4, ".otf")) {
282 fontPath
= new GooString(data
);
283 if (!(dataLen
>= 3 && data
[1] == ':' && data
[2] == '\\')) {
284 fontPath
->insert(0, '\\');
285 fontPath
->insert(0, winFontDir
);
286 fontPath
->append('\0');
291 p1
= strstr(p0
, " & ");
296 p1
= p0
+ strlen(p0
);
298 fonts
->append(makeWindowsFont(p0
, fontNum
,
299 fontPath
->getCString()));
312 SysFontInfo
*SysFontList::makeWindowsFont(char *name
, int fontNum
,
315 GBool bold
, italic
, oblique
, fixedWidth
;
320 GooString substituteName
;
323 bold
= italic
= oblique
= fixedWidth
= gFalse
;
325 // remove trailing ' (TrueType)'
326 if (n
> 11 && !strncmp(name
+ n
- 11, " (TrueType)", 11)) {
330 // remove trailing ' (OpenType)'
331 if (n
> 11 && !strncmp(name
+ n
- 11, " (OpenType)", 11)) {
335 // remove trailing ' Italic'
336 if (n
> 7 && !strncmp(name
+ n
- 7, " Italic", 7)) {
341 // remove trailing ' Oblique'
342 if (n
> 7 && !strncmp(name
+ n
- 8, " Oblique", 8)) {
347 // remove trailing ' Bold'
348 if (n
> 5 && !strncmp(name
+ n
- 5, " Bold", 5)) {
353 // remove trailing ' Regular'
354 if (n
> 5 && !strncmp(name
+ n
- 8, " Regular", 8)) {
358 // the familyname cannot indicate whether a font is fixedWidth or not.
359 // some well-known fixedWidth typeface family names or keyword are checked.
360 if ( strstr(name
, "Courier") ||
361 strstr(name
, "Fixed") ||
362 (strstr(name
, "Mono") && !strstr(name
, "Monotype")) ||
363 strstr(name
, "Typewriter") )
369 //----- normalize the font name
370 s
= new GooString(name
, n
);
372 while (i
< s
->getLength()) {
374 if (c
== ' ' || c
== ',' || c
== '-') {
381 if (!strcasecmp(path
+ strlen(path
) - 4, ".ttc")) {
387 return new SysFontInfo(s
, bold
, italic
, oblique
, fixedWidth
,
388 new GooString(path
), type
, fontNum
, substituteName
.copy());
391 static GooString
* replaceSuffix(GooString
*path
,
392 const char* suffixA
, const char* suffixB
)
394 int suffLenA
= strlen(suffixA
);
395 int suffLenB
= strlen(suffixB
);
396 int baseLenA
= path
->getLength() - suffLenA
;
397 int baseLenB
= path
->getLength() - suffLenB
;
399 if (!strcasecmp(path
->getCString()+baseLenA
, suffixA
)) {
400 path
->del(baseLenA
,suffLenA
)->append(suffixB
);
401 } else if (!strcasecmp(path
->getCString()+baseLenB
, suffixB
)) {
402 path
->del(baseLenB
,suffLenB
)->append(suffixA
);
409 void GlobalParams::setupBaseFonts(char * dir
)
411 const char *dataRoot
= popplerDataDir
? popplerDataDir
: POPPLER_DATADIR
;
412 GooString
*fileName
= NULL
;
415 if (baseFontsInitialized
)
417 baseFontsInitialized
= true;
419 char winFontDir
[MAX_PATH
];
420 GetWindowsFontDir(winFontDir
, sizeof(winFontDir
));
422 for (int i
= 0; displayFontTab
[i
].name
; ++i
) {
423 GooString
*fontName
= new GooString(displayFontTab
[i
].name
);
424 if (fontFiles
->lookup(fontName
))
428 GooString
*fontPath
= appendToPath(new GooString(dir
), displayFontTab
[i
].t1FileName
);
429 if (FileExists(fontPath
->getCString()) ||
430 FileExists(replaceSuffix(fontPath
, ".pfb", ".pfa")->getCString())) {
431 addFontFile(fontName
, fontPath
);
437 if (winFontDir
[0] && displayFontTab
[i
].ttFileName
) {
438 GooString
*fontPath
= appendToPath(new GooString(winFontDir
), displayFontTab
[i
].ttFileName
);
439 if (FileExists(fontPath
->getCString()) ||
440 FileExists(replaceSuffix(fontPath
, ".ttc", ".ttf")->getCString())) {
441 addFontFile(fontName
, fontPath
);
447 if (displayFontTab
[i
].warnIfMissing
) {
448 error(errSyntaxError
, -1, "No display font for '{0:s}'", displayFontTab
[i
].name
);
453 GooString
gooWinFontsDir(winFontDir
);
454 sysFonts
->scanWindowsFonts(&gooWinFontsDir
);
457 fileName
= new GooString(dataRoot
);
458 fileName
->append("/cidfmap");
461 file
= GooFile::open(fileName
);
468 parser
= new Parser(NULL
,
470 new FileStream(file
, 0, gFalse
, file
->size(), &obj1
)),
473 parser
->getObj(&obj1
);
474 while (!obj1
.isEOF()) {
475 parser
->getObj(&obj2
);
480 obj2
.getDict()->lookup("Path", &obj3
);
482 addFontFile(new GooString(obj1
.getName()), obj3
.getString()->copy());
485 } else if (obj2
.isName()) {
486 substFiles
->add(new GooString(obj1
.getName()), new GooString(obj2
.getName()));
491 parser
->getObj(&obj1
);
493 while (obj1
.isCmd(";")) {
495 parser
->getObj(&obj1
);
506 static const char *findSubstituteName(GfxFont
*font
, GooHash
*fontFiles
,
508 const char *origName
)
511 if (!origName
) return NULL
;
512 GooString
*name2
= new GooString(origName
);
513 int n
= strlen(origName
);
514 // remove trailing "-Identity-H"
515 if (n
> 11 && !strcmp(name2
->getCString() + n
- 11, "-Identity-H")) {
516 name2
->del(n
- 11, 11);
519 // remove trailing "-Identity-V"
520 if (n
> 11 && !strcmp(name2
->getCString() + n
- 11, "-Identity-V")) {
521 name2
->del(n
- 11, 11);
524 GooString
*substName
= (GooString
*)substFiles
->lookup(name2
);
525 if (substName
!= NULL
) {
527 return substName
->getCString();
530 /* TODO: try to at least guess bold/italic/bolditalic from the name */
532 if (font
->isCIDFont()) {
533 GooString
*collection
= ((GfxCIDFont
*)font
)->getCollection();
535 const char* name3
= NULL
;
536 if ( !collection
->cmp("Adobe-CNS1") )
537 name3
= DEFAULT_CID_FONT_AC1_MSWIN
;
538 else if ( !collection
->cmp("Adobe-GB1") )
539 name3
= DEFAULT_CID_FONT_AG1_MSWIN
;
540 else if ( !collection
->cmp("Adobe-Japan1") )
541 name3
= DEFAULT_CID_FONT_AJ1_MSWIN
;
542 else if ( !collection
->cmp("Adobe-Japan2") )
543 name3
= DEFAULT_CID_FONT_AJ2_MSWIN
;
544 else if ( !collection
->cmp("Adobe-Korea1") )
545 name3
= DEFAULT_CID_FONT_AK1_MSWIN
;
547 if (name3
&& fontFiles
->lookup(name3
))
550 if (fontFiles
->lookup(DEFAULT_CID_FONT_MSWIN
))
551 return DEFAULT_CID_FONT_MSWIN
;
553 return DEFAULT_SUBSTITUTE_FONT
;
556 /* Windows implementation of external font matching code */
557 GooString
*GlobalParams::findSystemFontFile(GfxFont
*font
,
559 int *fontNum
, GooString
*substituteFontName
,
560 GooString
*base14Name
) {
562 GooString
*path
= NULL
;
563 GooString
*fontName
= font
->getName();
564 if (!fontName
) return NULL
;
566 setupBaseFonts(NULL
);
568 // TODO: base14Name should be changed?
569 // In the system using FontConfig, findSystemFontFile() uses
570 // base14Name only for the creation of query pattern.
572 if ((fi
= sysFonts
->find(fontName
, gFalse
, gFalse
))) {
573 path
= fi
->path
->copy();
575 *fontNum
= fi
->fontNum
;
576 if (substituteFontName
)
577 substituteFontName
->Set(fi
->substituteName
->getCString());
579 GooString
*substFontName
= new GooString(findSubstituteName(font
, fontFiles
,
581 fontName
->getCString()));
582 GooString
*path2
= NULL
;
583 error(errSyntaxError
, -1, "Couldn't find a font for '{0:t}', subst is '{1:t}'", fontName
, substFontName
);
584 if ((path2
= (GooString
*)fontFiles
->lookup(substFontName
))) {
585 path
= new GooString(path2
);
586 if (substituteFontName
)
587 substituteFontName
->Set(path
->getCString());
588 if (!strcasecmp(path
->getCString() + path
->getLength() - 4, ".ttc")) {