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