1 /****************************************************************************\
2  Part of the XeTeX typesetting system
3  Copyright (c) 1994-2008 by SIL International
4  Copyright (c) 2009, 2011 by Jonathan Kew
5 
6  SIL Author(s): Jonathan Kew
7 
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15 
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
23 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 
27 Except as contained in this notice, the name of the copyright holders
28 shall not be used in advertising or otherwise to promote the sale,
29 use or other dealings in this Software without prior written
30 authorization from the copyright holders.
31 \****************************************************************************/
32 
33 #include <w2c/config.h>
34 
35 #include "XeTeXFontMgr_FC.h"
36 
37 /* allow compilation with old Fontconfig header */
38 #ifndef FC_FULLNAME
39 #define FC_FULLNAME "fullname"
40 #endif
41 
42 #include FT_SFNT_NAMES_H
43 #include FT_TRUETYPE_IDS_H
44 
45 #include <unicode/ucnv.h>
46 
47 #define kFontFamilyName 1
48 #define kFontStyleName  2
49 #define kFontFullName   4
50 #define kPreferredFamilyName    16
51 #define kPreferredSubfamilyName 17
52 
53 static UConverter* macRomanConv = NULL;
54 static UConverter* utf16beConv = NULL;
55 static UConverter* utf8Conv = NULL;
56 
57 static char*
convertToUtf8(UConverter * conv,const unsigned char * name,int len)58 convertToUtf8(UConverter* conv, const unsigned char* name, int len)
59 {
60     char* buffer1 = NULL;
61     char* buffer2 = NULL;
62     int bufSize = -1;
63 
64     if (2 * (len + 1) > bufSize) {
65         if (buffer1 != NULL) {
66             delete[] buffer1;
67             delete[] buffer2;
68         }
69         bufSize = 2 * len + 100;
70         buffer1 = new char[bufSize];
71         buffer2 = new char[bufSize];
72     }
73 
74     UErrorCode status = U_ZERO_ERROR;
75     len = ucnv_toUChars(conv, (UChar*)buffer1, bufSize, (const char*)name, len, &status);
76     len = ucnv_fromUChars(utf8Conv, buffer2, bufSize, (UChar*)buffer1, len, &status);
77     buffer2[len] = 0;
78 
79     delete[] buffer1;
80     return buffer2;
81 }
82 
83 XeTeXFontMgr::NameCollection*
readNames(FcPattern * pat)84 XeTeXFontMgr_FC::readNames(FcPattern* pat)
85 {
86     NameCollection* names = new NameCollection;
87 
88     char* pathname;
89     if (FcPatternGetString(pat, FC_FILE, 0, (FcChar8**)&pathname) != FcResultMatch)
90         return names;
91     int index;
92     if (FcPatternGetInteger(pat, FC_INDEX, 0, &index) != FcResultMatch)
93         return names;
94 
95     FT_Face face;
96     if (FT_New_Face(gFreeTypeLibrary, pathname, index, &face) != 0)
97         return names;
98 
99     const char* name = FT_Get_Postscript_Name(face);
100     if (name == NULL)
101         return names;
102     names->m_psName = name;
103 
104     // for sfnt containers, we'll read the name table ourselves, not rely on Fontconfig
105     if (FT_IS_SFNT(face)) {
106         std::list<std::string>  familyNames;
107         std::list<std::string>  subFamilyNames;
108         FT_SfntName nameRec;
109         for (index = 0; index < FT_Get_Sfnt_Name_Count(face); ++index) {
110             char* utf8name = NULL;
111             if (FT_Get_Sfnt_Name(face, index, &nameRec) != 0)
112                 continue;
113             switch (nameRec.name_id) {
114                 case kFontFullName:
115                 case kFontFamilyName:
116                 case kFontStyleName:
117                 case kPreferredFamilyName:
118                 case kPreferredSubfamilyName:
119                     {
120                         bool preferredName = false;
121                         if (nameRec.platform_id == TT_PLATFORM_MACINTOSH
122                                 && nameRec.encoding_id == TT_MAC_ID_ROMAN && nameRec.language_id == 0) {
123                             utf8name = convertToUtf8(macRomanConv, nameRec.string, nameRec.string_len);
124                             preferredName = true;
125                         }
126                         else if ((nameRec.platform_id == TT_PLATFORM_APPLE_UNICODE)
127                                 || (nameRec.platform_id == TT_PLATFORM_MICROSOFT))
128                             utf8name = convertToUtf8(utf16beConv, nameRec.string, nameRec.string_len);
129 
130                         if (utf8name != NULL) {
131                             std::list<std::string>* nameList = NULL;
132                             switch (nameRec.name_id) {
133                                 case kFontFullName:
134                                     nameList = &names->m_fullNames;
135                                     break;
136                                 case kFontFamilyName:
137                                     nameList = &names->m_familyNames;
138                                     break;
139                                 case kFontStyleName:
140                                     nameList = &names->m_styleNames;
141                                     break;
142                                 case kPreferredFamilyName:
143                                     nameList = &familyNames;
144                                     break;
145                                 case kPreferredSubfamilyName:
146                                     nameList = &subFamilyNames;
147                                     break;
148                             }
149                             if (preferredName)
150                                 prependToList(nameList, utf8name);
151                             else
152                                 appendToList(nameList, utf8name);
153                             delete[] utf8name;
154                         }
155                     }
156                     break;
157             }
158         }
159         if (familyNames.size() > 0)
160             names->m_familyNames = familyNames;
161         if (subFamilyNames.size() > 0)
162             names->m_styleNames = subFamilyNames;
163     } else {
164         index = 0;
165         while (FcPatternGetString(pat, FC_FULLNAME, index++, (FcChar8**)&name) == FcResultMatch)
166             appendToList(&names->m_fullNames, name);
167         index = 0;
168         while (FcPatternGetString(pat, FC_FAMILY, index++, (FcChar8**)&name) == FcResultMatch)
169             appendToList(&names->m_familyNames, name);
170         index = 0;
171         while (FcPatternGetString(pat, FC_STYLE, index++, (FcChar8**)&name) == FcResultMatch)
172             appendToList(&names->m_styleNames, name);
173 
174         if (names->m_fullNames.size() == 0) {
175             std::string fullName(names->m_familyNames.front());
176             if (names->m_styleNames.size() > 0) {
177                 fullName += " ";
178                 fullName += names->m_styleNames.front();
179             }
180             names->m_fullNames.push_back(fullName);
181         }
182     }
183 
184     FT_Done_Face(face);
185 
186     return names;
187 }
188 
189 void
getOpSizeRecAndStyleFlags(Font * theFont)190 XeTeXFontMgr_FC::getOpSizeRecAndStyleFlags(Font* theFont)
191 {
192     XeTeXFontMgr::getOpSizeRecAndStyleFlags(theFont);
193 
194     if (theFont->weight == 0 && theFont->width == 0) {
195         // try to get values from FontConfig, as it apparently wasn't an sfnt
196         FcPattern* pat = theFont->fontRef;
197         int value;
198         if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &value) == FcResultMatch)
199             theFont->weight = value;
200         if (FcPatternGetInteger(pat, FC_WIDTH, 0, &value) == FcResultMatch)
201             theFont->width = value;
202         if (FcPatternGetInteger(pat, FC_SLANT, 0, &value) == FcResultMatch)
203             theFont->slant = value;
204     }
205 }
206 
207 void
cacheFamilyMembers(const std::list<std::string> & familyNames)208 XeTeXFontMgr_FC::cacheFamilyMembers(const std::list<std::string>& familyNames)
209 {
210     if (familyNames.size() == 0)
211         return;
212     for (int f = 0; f < allFonts->nfont; ++f) {
213         FcPattern* pat = allFonts->fonts[f];
214         if (m_platformRefToFont.find(pat) != m_platformRefToFont.end())
215             continue;
216         char* s;
217         for (int i = 0; FcPatternGetString(pat, FC_FAMILY, i, (FcChar8**)&s) == FcResultMatch; ++i) {
218             for (std::list<std::string>::const_iterator j = familyNames.begin(); j != familyNames.end(); ++j) {
219                 if (*j == s) {
220                     NameCollection* names = readNames(pat);
221                     addToMaps(pat, names);
222                     delete names;
223                     goto cached;
224                 }
225             }
226         }
227     cached:
228         ;
229     }
230 }
231 
232 void
searchForHostPlatformFonts(const std::string & name)233 XeTeXFontMgr_FC::searchForHostPlatformFonts(const std::string& name)
234 {
235     if (cachedAll) // we've already loaded everything on an earlier search
236         return;
237 
238     std::string famName;
239     int hyph = name.find('-');
240     if (hyph > 0 && hyph < name.length() - 1)
241         famName.assign(name.begin(), name.begin() + hyph);
242     else
243         hyph = 0;
244 
245     bool found = false;
246     while (1) {
247         for (int f = 0; f < allFonts->nfont; ++f) {
248             FcPattern* pat = allFonts->fonts[f];
249             if (m_platformRefToFont.find(pat) != m_platformRefToFont.end())
250                 continue;
251 
252             if (cachedAll) {
253                 // failed to find it via FC; add everything to our maps (potentially slow) as a last resort
254                 NameCollection* names = readNames(pat);
255                 addToMaps(pat, names);
256                 delete names;
257                 continue;
258             }
259 
260             char* s;
261             int i;
262             for (i = 0; FcPatternGetString(pat, FC_FULLNAME, i, (FcChar8**)&s) == FcResultMatch; ++i) {
263                 if (name == s) {
264                     NameCollection* names = readNames(pat);
265                     addToMaps(pat, names);
266                     cacheFamilyMembers(names->m_familyNames);
267                     delete names;
268                     found = true;
269                     goto next_font;
270                 }
271             }
272 
273             for (i = 0; FcPatternGetString(pat, FC_FAMILY, i, (FcChar8**)&s) == FcResultMatch; ++i) {
274                 if (name == s || (hyph && famName == s)) {
275                     NameCollection* names = readNames(pat);
276                     addToMaps(pat, names);
277                     cacheFamilyMembers(names->m_familyNames);
278                     delete names;
279                     found = true;
280                     goto next_font;
281                 }
282                 char* t;
283                 for (int j = 0; FcPatternGetString(pat, FC_STYLE, j, (FcChar8**)&t) == FcResultMatch; ++j) {
284                     std::string full(s);
285                     full += " ";
286                     full += t;
287                     if (name == full) {
288                         NameCollection* names = readNames(pat);
289                         addToMaps(pat, names);
290                         cacheFamilyMembers(names->m_familyNames);
291                         delete names;
292                         found = true;
293                         goto next_font;
294                     }
295                 }
296             }
297 
298         next_font:
299             ;
300         }
301 
302         if (found || cachedAll)
303             break;
304         cachedAll = true;
305     }
306 }
307 
308 void
initialize()309 XeTeXFontMgr_FC::initialize()
310 {
311     if (FcInit() == FcFalse) {
312         fprintf(stderr, "fontconfig initialization failed!\n");
313         exit(9);
314     }
315 
316     if (gFreeTypeLibrary == 0 && FT_Init_FreeType(&gFreeTypeLibrary) != 0) {
317         fprintf(stderr, "FreeType initialization failed!\n");
318         exit(9);
319     }
320 
321     UErrorCode err = U_ZERO_ERROR;
322     macRomanConv = ucnv_open("macintosh", &err);
323     utf16beConv = ucnv_open("UTF16BE", &err);
324     utf8Conv = ucnv_open("UTF8", &err);
325     if (err != 0) {
326         fprintf(stderr, "internal error; cannot read font names\n");
327         exit(3);
328     }
329 
330     FcPattern* pat = FcNameParse((const FcChar8*)":outline=true");
331     FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_INDEX,
332                                        FC_FULLNAME, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_FONTFORMAT, NULL);
333     allFonts = FcFontList(FcConfigGetCurrent(), pat, os);
334     FcObjectSetDestroy(os);
335     FcPatternDestroy(pat);
336 
337     cachedAll = false;
338 }
339 
340 void
terminate()341 XeTeXFontMgr_FC::terminate()
342 {
343     if (macRomanConv != NULL)
344         ucnv_close(macRomanConv);
345     if (utf16beConv != NULL)
346         ucnv_close(utf16beConv);
347     if (utf8Conv != NULL)
348         ucnv_close(utf8Conv);
349 }
350 
351 std::string
getPlatformFontDesc(PlatformFontRef font) const352 XeTeXFontMgr_FC::getPlatformFontDesc(PlatformFontRef font) const
353 {
354     std::string path;
355     FcChar8* s;
356     if (FcPatternGetString(font, FC_FILE, 0, (FcChar8**)&s) == FcResultMatch)
357         path = (char*)s;
358     else
359         path = "[unknown]";
360     return path;
361 }
362 
363