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>
11 TODO: instead of a fixed mapping defined in displayFontTab, it could
12 scan the whole fonts directory, parse TTF files and build font
13 description for all fonts available in Windows. That's how MuPDF works.
20 #ifdef USE_GCC_PRAGMAS
21 #pragma implementation
25 #if !(_WIN32_IE >= 0x0500)
26 #error "_WIN32_IE must be defined >= 0x0500 for SHGFP_TYPE_CURRENT from shlobj.h"
35 #include "goo/GooString.h"
36 #include "goo/GooList.h"
37 #include "goo/GooHash.h"
38 #include "goo/gfile.h"
40 #include "NameToCharCode.h"
41 #include "CharCodeToUnicode.h"
42 #include "UnicodeMap.h"
44 #include "BuiltinFontTables.h"
45 #include "FontEncodingTables.h"
46 #include "GlobalParams.h"
55 # define lockGlobalParams gLockMutex(&mutex)
56 # define lockUnicodeMapCache gLockMutex(&unicodeMapCacheMutex)
57 # define lockCMapCache gLockMutex(&cMapCacheMutex)
58 # define unlockGlobalParams gUnlockMutex(&mutex)
59 # define unlockUnicodeMapCache gUnlockMutex(&unicodeMapCacheMutex)
60 # define unlockCMapCache gUnlockMutex(&cMapCacheMutex)
62 # define lockGlobalParams
63 # define lockUnicodeMapCache
64 # define lockCMapCache
65 # define unlockGlobalParams
66 # define unlockUnicodeMapCache
67 # define unlockCMapCache
70 #define DEFAULT_SUBSTITUTE_FONT "Helvetica"
71 #define DEFAULT_CID_FONT_AC1_MSWIN "MingLiU" /* Adobe-CNS1 for Taiwan, HongKong */
72 #define DEFAULT_CID_FONT_AG1_MSWIN "SimSun" /* Adobe-GB1 for PRC, Singapore */
73 #define DEFAULT_CID_FONT_AJ1_MSWIN "MS-Mincho" /* Adobe-Japan1 */
74 #define DEFAULT_CID_FONT_AJ2_MSWIN "MS-Mincho" /* Adobe-Japan2 (legacy) */
75 #define DEFAULT_CID_FONT_AK1_MSWIN "Batang" /* Adobe-Korea1 */
76 #define DEFAULT_CID_FONT_MSWIN "ArialUnicode" /* Unknown */
80 const char *t1FileName
;
81 const char *ttFileName
;
83 } displayFontTab
[] = {
84 {"Courier", "n022003l.pfb", "cour.ttf", gTrue
},
85 {"Courier-Bold", "n022004l.pfb", "courbd.ttf", gTrue
},
86 {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", gTrue
},
87 {"Courier-Oblique", "n022023l.pfb", "couri.ttf", gTrue
},
88 {"Helvetica", "n019003l.pfb", "arial.ttf", gTrue
},
89 {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", gTrue
},
90 {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", gTrue
},
91 {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", gTrue
},
92 // TODO: not sure if "symbol.ttf" is right
93 {"Symbol", "s050000l.pfb", "symbol.ttf", gTrue
},
94 {"Times-Bold", "n021004l.pfb", "timesbd.ttf", gTrue
},
95 {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", gTrue
},
96 {"Times-Italic", "n021023l.pfb", "timesi.ttf", gTrue
},
97 {"Times-Roman", "n021003l.pfb", "times.ttf", gTrue
},
98 // TODO: not sure if "wingding.ttf" is right
99 {"ZapfDingbats", "d050000l.pfb", "wingding.ttf", gTrue
},
101 // those seem to be frequently accessed by PDF files and I kind of guess
102 // which font file do the refer to
103 {"Palatino", NULL
, "pala.ttf", gTrue
},
104 {"Palatino-Roman", NULL
, "pala.ttf", gTrue
},
105 {"Palatino-Bold", NULL
, "palab.ttf", gTrue
},
106 {"Palatino-Italic", NULL
, "palai.ttf", gTrue
},
107 {"Palatino,Italic", NULL
, "palai.ttf", gTrue
},
108 {"Palatino-BoldItalic", NULL
, "palabi.ttf", gTrue
},
110 {"ArialBlack", NULL
, "arialbd.ttf", gTrue
},
112 {"ArialNarrow", NULL
, "arialn.ttf", gTrue
},
113 {"ArialNarrow,Bold", NULL
, "arialnb.ttf", gTrue
},
114 {"ArialNarrow,Italic", NULL
, "arialni.ttf", gTrue
},
115 {"ArialNarrow,BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
116 {"ArialNarrow-Bold", NULL
, "arialnb.ttf", gTrue
},
117 {"ArialNarrow-Italic", NULL
, "arialni.ttf", gTrue
},
118 {"ArialNarrow-BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
120 {"HelveticaNarrow", NULL
, "arialn.ttf", gTrue
},
121 {"HelveticaNarrow,Bold", NULL
, "arialnb.ttf", gTrue
},
122 {"HelveticaNarrow,Italic", NULL
, "arialni.ttf", gTrue
},
123 {"HelveticaNarrow,BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
124 {"HelveticaNarrow-Bold", NULL
, "arialnb.ttf", gTrue
},
125 {"HelveticaNarrow-Italic", NULL
, "arialni.ttf", gTrue
},
126 {"HelveticaNarrow-BoldItalic", NULL
, "arialnbi.ttf", gTrue
},
128 {"BookAntiqua", NULL
, "bkant.ttf", gTrue
},
129 {"BookAntiqua,Bold", NULL
, "bkant.ttf", gTrue
},
130 {"BookAntiqua,Italic", NULL
, "bkant.ttf", gTrue
},
131 {"BookAntiqua,BoldItalic", NULL
, "bkant.ttf", gTrue
},
132 {"BookAntiqua-Bold", NULL
, "bkant.ttf", gTrue
},
133 {"BookAntiqua-Italic", NULL
, "bkant.ttf", gTrue
},
134 {"BookAntiqua-BoldItalic", NULL
, "bkant.ttf", gTrue
},
136 {"Verdana", NULL
, "verdana.ttf", gTrue
},
137 {"Verdana,Bold", NULL
, "verdanab.ttf", gTrue
},
138 {"Verdana,Italic", NULL
, "verdanai.ttf", gTrue
},
139 {"Verdana,BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
140 {"Verdana-Bold", NULL
, "verdanab.ttf", gTrue
},
141 {"Verdana-Italic", NULL
, "verdanai.ttf", gTrue
},
142 {"Verdana-BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
144 {"Tahoma", NULL
, "tahoma.ttf", gTrue
},
145 {"Tahoma,Bold", NULL
, "tahomabd.ttf", gTrue
},
146 {"Tahoma,Italic", NULL
, "tahoma.ttf", gTrue
},
147 {"Tahoma,BoldItalic", NULL
, "tahomabd.ttf", gTrue
},
148 {"Tahoma-Bold", NULL
, "tahomabd.ttf", gTrue
},
149 {"Tahoma-Italic", NULL
, "tahoma.ttf", gTrue
},
150 {"Tahoma-BoldItalic", NULL
, "tahomabd.ttf", gTrue
},
152 {"CCRIKH+Verdana", NULL
, "verdana.ttf", gTrue
},
153 {"CCRIKH+Verdana,Bold", NULL
, "verdanab.ttf", gTrue
},
154 {"CCRIKH+Verdana,Italic", NULL
, "verdanai.ttf", gTrue
},
155 {"CCRIKH+Verdana,BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
156 {"CCRIKH+Verdana-Bold", NULL
, "verdanab.ttf", gTrue
},
157 {"CCRIKH+Verdana-Italic", NULL
, "verdanai.ttf", gTrue
},
158 {"CCRIKH+Verdana-BoldItalic", NULL
, "verdanaz.ttf", gTrue
},
160 {"Georgia", NULL
, "georgia.ttf", gTrue
},
161 {"Georgia,Bold", NULL
, "georgiab.ttf", gTrue
},
162 {"Georgia,Italic", NULL
, "georgiai.ttf", gTrue
},
163 {"Georgia,BoldItalic", NULL
, "georgiaz.ttf", gTrue
},
164 {"Georgia-Bold", NULL
, "georgiab.ttf", gTrue
},
165 {"Georgia-Italic", NULL
, "georgiai.ttf", gTrue
},
166 {"Georgia-BoldItalic", NULL
, "georgiaz.ttf", gTrue
},
168 // fallback for Adobe CID fonts:
169 {"MingLiU", NULL
, "mingliu.ttf", gFalse
},
170 {"SimSun", NULL
, "simsun.ttf", gFalse
},
171 {"MS-Mincho", NULL
, "msmincho.ttf", gFalse
},
172 {"Batang", NULL
, "batang.ttf", gFalse
},
173 {"ArialUnicode", NULL
, "arialuni.ttf", gTrue
},
177 #define FONTS_SUBDIR "\\fonts"
179 static void GetWindowsFontDir(char *winFontDir
, int cbWinFontDirLen
)
181 BOOL (__stdcall
*SHGetSpecialFolderPathFunc
)(HWND hwndOwner
,
185 HRESULT (__stdcall
*SHGetFolderPathFunc
)(HWND hwndOwner
,
191 // SHGetSpecialFolderPath isn't available in older versions of shell32.dll (Win95 and
192 // WinNT4), so do a dynamic load of ANSI versions.
193 winFontDir
[0] = '\0';
195 HMODULE hLib
= LoadLibrary("shell32.dll");
197 SHGetFolderPathFunc
= (HRESULT (__stdcall
*)(HWND
, int, HANDLE
, DWORD
, LPTSTR
))
198 GetProcAddress(hLib
, "SHGetFolderPathA");
199 if (SHGetFolderPathFunc
)
200 (*SHGetFolderPathFunc
)(NULL
, CSIDL_FONTS
, NULL
, SHGFP_TYPE_CURRENT
, winFontDir
);
202 if (!winFontDir
[0]) {
203 // Try an older function
204 SHGetSpecialFolderPathFunc
= (BOOL (__stdcall
*)(HWND
, LPTSTR
, int, BOOL
))
205 GetProcAddress(hLib
, "SHGetSpecialFolderPathA");
206 if (SHGetSpecialFolderPathFunc
)
207 (*SHGetSpecialFolderPathFunc
)(NULL
, winFontDir
, CSIDL_FONTS
, FALSE
);
215 hLib
= LoadLibrary("SHFolder.dll");
217 SHGetFolderPathFunc
= (HRESULT (__stdcall
*)(HWND
, int, HANDLE
, DWORD
, LPTSTR
))
218 GetProcAddress(hLib
, "SHGetFolderPathA");
219 if (SHGetFolderPathFunc
)
220 (*SHGetFolderPathFunc
)(NULL
, CSIDL_FONTS
, NULL
, SHGFP_TYPE_CURRENT
, winFontDir
);
226 // Everything else failed so the standard fonts directory.
227 GetWindowsDirectory(winFontDir
, cbWinFontDirLen
);
229 strncat(winFontDir
, FONTS_SUBDIR
, cbWinFontDirLen
);
230 winFontDir
[cbWinFontDirLen
-1] = 0;
234 static bool FileExists(const char *path
)
236 FILE * f
= fopen(path
, "rb");
244 void SysFontList::scanWindowsFonts(GooString
*winFontDir
) {
245 OSVERSIONINFO version
;
247 DWORD idx
, valNameLen
, dataLen
, type
;
249 char valName
[1024], data
[1024];
254 version
.dwOSVersionInfoSize
= sizeof(version
);
255 GetVersionEx(&version
);
256 if (version
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
257 path
= "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\";
259 path
= "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
261 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, path
, 0,
262 KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
,
263 ®Key
) == ERROR_SUCCESS
) {
266 valNameLen
= sizeof(valName
) - 1;
267 dataLen
= sizeof(data
) - 1;
268 if (RegEnumValue(regKey
, idx
, valName
, &valNameLen
, NULL
,
269 &type
, (LPBYTE
)data
, &dataLen
) != ERROR_SUCCESS
) {
272 if (type
== REG_SZ
&&
273 valNameLen
> 0 && valNameLen
< sizeof(valName
) &&
274 dataLen
> 0 && dataLen
< sizeof(data
)) {
275 valName
[valNameLen
] = '\0';
276 data
[dataLen
] = '\0';
278 if (!strcasecmp(data
+ n
- 4, ".ttf") ||
279 !strcasecmp(data
+ n
- 4, ".ttc") ||
280 !strcasecmp(data
+ n
- 4, ".otf")) {
281 fontPath
= new GooString(data
);
282 if (!(dataLen
>= 3 && data
[1] == ':' && data
[2] == '\\')) {
283 fontPath
->insert(0, '\\');
284 fontPath
->insert(0, winFontDir
);
285 fontPath
->append('\0');
290 p1
= strstr(p0
, " & ");
295 p1
= p0
+ strlen(p0
);
297 fonts
->append(makeWindowsFont(p0
, fontNum
,
298 fontPath
->getCString()));
311 SysFontInfo
*SysFontList::makeWindowsFont(char *name
, int fontNum
,
314 GBool bold
, italic
, oblique
, fixedWidth
;
319 GooString substituteName
;
322 bold
= italic
= oblique
= fixedWidth
= gFalse
;
324 // remove trailing ' (TrueType)'
325 if (n
> 11 && !strncmp(name
+ n
- 11, " (TrueType)", 11)) {
329 // remove trailing ' (OpenType)'
330 if (n
> 11 && !strncmp(name
+ n
- 11, " (OpenType)", 11)) {
334 // remove trailing ' Italic'
335 if (n
> 7 && !strncmp(name
+ n
- 7, " Italic", 7)) {
340 // remove trailing ' Oblique'
341 if (n
> 7 && !strncmp(name
+ n
- 8, " Oblique", 8)) {
346 // remove trailing ' Bold'
347 if (n
> 5 && !strncmp(name
+ n
- 5, " Bold", 5)) {
352 // remove trailing ' Regular'
353 if (n
> 5 && !strncmp(name
+ n
- 8, " Regular", 8)) {
357 // the familyname cannot indicate whether a font is fixedWidth or not.
358 // some well-known fixedWidth typeface family names or keyword are checked.
359 if ( strstr(name
, "Courier") ||
360 strstr(name
, "Fixed") ||
361 (strstr(name
, "Mono") && !strstr(name
, "Monotype")) ||
362 strstr(name
, "Typewriter") )
368 //----- normalize the font name
369 s
= new GooString(name
, n
);
371 while (i
< s
->getLength()) {
373 if (c
== ' ' || c
== ',' || c
== '-') {
380 if (!strcasecmp(path
+ strlen(path
) - 4, ".ttc")) {
386 return new SysFontInfo(s
, bold
, italic
, oblique
, fixedWidth
,
387 new GooString(path
), type
, fontNum
, substituteName
.copy());
390 static GooString
* replaceSuffix(GooString
*path
,
391 const char* suffixA
, const char* suffixB
)
393 int suffLenA
= strlen(suffixA
);
394 int suffLenB
= strlen(suffixB
);
395 int baseLenA
= path
->getLength() - suffLenA
;
396 int baseLenB
= path
->getLength() - suffLenB
;
398 if (!strcasecmp(path
->getCString()+baseLenA
, suffixA
)) {
399 path
->del(baseLenA
,suffLenA
)->append(suffixB
);
400 } else if (!strcasecmp(path
->getCString()+baseLenB
, suffixB
)) {
401 path
->del(baseLenB
,suffLenB
)->append(suffixA
);
408 void GlobalParams::setupBaseFonts(char * dir
)
410 const char *dataRoot
= popplerDataDir
? popplerDataDir
: POPPLER_DATADIR
;
411 GooString
*fileName
= NULL
;
414 if (baseFontsInitialized
)
416 baseFontsInitialized
= true;
418 char winFontDir
[MAX_PATH
];
419 GetWindowsFontDir(winFontDir
, sizeof(winFontDir
));
421 for (int i
= 0; displayFontTab
[i
].name
; ++i
) {
422 GooString
*fontName
= new GooString(displayFontTab
[i
].name
);
423 if (fontFiles
->lookup(fontName
))
427 GooString
*fontPath
= appendToPath(new GooString(dir
), displayFontTab
[i
].t1FileName
);
428 if (FileExists(fontPath
->getCString()) ||
429 FileExists(replaceSuffix(fontPath
, ".pfb", ".pfa")->getCString())) {
430 addFontFile(fontName
, fontPath
);
436 if (winFontDir
[0] && displayFontTab
[i
].ttFileName
) {
437 GooString
*fontPath
= appendToPath(new GooString(winFontDir
), displayFontTab
[i
].ttFileName
);
438 if (FileExists(fontPath
->getCString()) ||
439 FileExists(replaceSuffix(fontPath
, ".ttc", ".ttf")->getCString())) {
440 addFontFile(fontName
, fontPath
);
446 if (displayFontTab
[i
].warnIfMissing
)
447 error(errSyntaxError
, -1, "No display font for '{0:s}'", fontName
);
450 sysFonts
->scanWindowsFonts(new GooString(winFontDir
));
453 fileName
= new GooString(dataRoot
);
454 fileName
->append("/cidfmap");
457 file
= GooFile::open(fileName
);
464 parser
= new Parser(NULL
,
466 new FileStream(file
, 0, gFalse
, file
->size(), &obj1
)),
469 parser
->getObj(&obj1
);
470 while (!obj1
.isEOF()) {
471 parser
->getObj(&obj2
);
476 obj2
.getDict()->lookup("Path", &obj3
);
478 addFontFile(new GooString(obj1
.getName()), obj3
.getString()->copy());
481 } else if (obj2
.isName()) {
482 substFiles
->add(new GooString(obj1
.getName()), new GooString(obj2
.getName()));
487 parser
->getObj(&obj1
);
489 while (obj1
.isCmd(";")) {
491 parser
->getObj(&obj1
);
499 static const char *findSubstituteName(GfxFont
*font
, GooHash
*fontFiles
,
501 const char *origName
)
504 if (!origName
) return NULL
;
505 GooString
*name2
= new GooString(origName
);
506 int n
= strlen(origName
);
507 // remove trailing "-Identity-H"
508 if (n
> 11 && !strcmp(name2
->getCString() + n
- 11, "-Identity-H")) {
509 name2
->del(n
- 11, 11);
512 // remove trailing "-Identity-V"
513 if (n
> 11 && !strcmp(name2
->getCString() + n
- 11, "-Identity-V")) {
514 name2
->del(n
- 11, 11);
517 GooString
*substName
= (GooString
*)substFiles
->lookup(name2
);
518 if (substName
!= NULL
) {
520 return substName
->getCString();
523 /* TODO: try to at least guess bold/italic/bolditalic from the name */
525 if (font
->isCIDFont()) {
526 GooString
*collection
= ((GfxCIDFont
*)font
)->getCollection();
528 const char* name3
= NULL
;
529 if ( !collection
->cmp("Adobe-CNS1") )
530 name3
= DEFAULT_CID_FONT_AC1_MSWIN
;
531 else if ( !collection
->cmp("Adobe-GB1") )
532 name3
= DEFAULT_CID_FONT_AG1_MSWIN
;
533 else if ( !collection
->cmp("Adobe-Japan1") )
534 name3
= DEFAULT_CID_FONT_AJ1_MSWIN
;
535 else if ( !collection
->cmp("Adobe-Japan2") )
536 name3
= DEFAULT_CID_FONT_AJ2_MSWIN
;
537 else if ( !collection
->cmp("Adobe-Korea1") )
538 name3
= DEFAULT_CID_FONT_AK1_MSWIN
;
540 if (name3
&& fontFiles
->lookup(name3
))
543 if (fontFiles
->lookup(DEFAULT_CID_FONT_MSWIN
))
544 return DEFAULT_CID_FONT_MSWIN
;
546 return DEFAULT_SUBSTITUTE_FONT
;
549 /* Windows implementation of external font matching code */
550 GooString
*GlobalParams::findSystemFontFile(GfxFont
*font
,
552 int *fontNum
, GooString
*substituteFontName
,
553 GooString
*base14Name
) {
555 GooString
*path
= NULL
;
556 GooString
*fontName
= font
->getName();
557 if (!fontName
) return NULL
;
559 setupBaseFonts(NULL
);
561 // TODO: base14Name should be changed?
562 // In the system using FontConfig, findSystemFontFile() uses
563 // base14Name only for the creation of query pattern.
565 if ((fi
= sysFonts
->find(fontName
, gFalse
, gFalse
))) {
566 path
= fi
->path
->copy();
568 *fontNum
= fi
->fontNum
;
569 if (substituteFontName
)
570 substituteFontName
->Set(fi
->substituteName
->getCString());
572 GooString
*substFontName
= new GooString(findSubstituteName(font
, fontFiles
,
574 fontName
->getCString()));
575 GooString
*path2
= NULL
;
576 error(errSyntaxError
, -1, "Couldn't find a font for '{0:t}', subst is '{1:t}'", fontName
, substFontName
);
577 if ((path2
= (GooString
*)fontFiles
->lookup(substFontName
))) {
578 path
= new GooString(path2
);
579 if (substituteFontName
)
580 substituteFontName
->Set(path
->getCString());
581 if (!strcasecmp(path
->getCString() + path
->getLength() - 4, ".ttc")) {