OLD | NEW |
| (Empty) |
1 /* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp | |
2 ** | |
3 ** Copyright 2009, Google Inc. | |
4 ** | |
5 ** Licensed under the Apache License, Version 2.0 (the "License"); | |
6 ** you may not use this file except in compliance with the License. | |
7 ** You may obtain a copy of the License at | |
8 ** | |
9 ** http://www.apache.org/licenses/LICENSE-2.0 | |
10 ** | |
11 ** Unless required by applicable law or agreed to in writing, software | |
12 ** distributed under the License is distributed on an "AS IS" BASIS, | |
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 ** See the License for the specific language governing permissions and | |
15 ** limitations under the License. | |
16 */ | |
17 | |
18 #include "skia/ext/SkFontHost_fontconfig_direct.h" | |
19 | |
20 #include <unistd.h> | |
21 #include <fcntl.h> | |
22 | |
23 #include <fontconfig/fontconfig.h> | |
24 | |
25 #include "third_party/skia/include/core/SkTypeface.h" | |
26 #include "third_party/skia/include/core/SkUtils.h" | |
27 | |
28 namespace { | |
29 | |
30 // Equivalence classes, used to match the Liberation and other fonts | |
31 // with their metric-compatible replacements. See the discussion in | |
32 // GetFontEquivClass(). | |
33 enum FontEquivClass | |
34 { | |
35 OTHER, | |
36 SANS, | |
37 SERIF, | |
38 MONO, | |
39 SYMBOL, | |
40 PGOTHIC, | |
41 GOTHIC, | |
42 PMINCHO, | |
43 MINCHO, | |
44 SIMSUN, | |
45 NSIMSUN, | |
46 SIMHEI, | |
47 PMINGLIU, | |
48 MINGLIU, | |
49 PMINGLIUHK, | |
50 MINGLIUHK, | |
51 }; | |
52 | |
53 // Match the font name against a whilelist of fonts, returning the equivalence | |
54 // class. | |
55 FontEquivClass GetFontEquivClass(const char* fontname) | |
56 { | |
57 // It would be nice for fontconfig to tell us whether a given suggested | |
58 // replacement is a "strong" match (that is, an equivalent font) or | |
59 // a "weak" match (that is, fontconfig's next-best attempt at finding a | |
60 // substitute). However, I played around with the fontconfig API for | |
61 // a good few hours and could not make it reveal this information. | |
62 // | |
63 // So instead, we hardcode. Initially this function emulated | |
64 // /etc/fonts/conf.d/30-metric-aliases.conf | |
65 // from my Ubuntu system, but we're better off being very conservative. | |
66 | |
67 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with | |
68 // Arial, Times New Roman and Courier New with a character repertoire | |
69 // much larger than Liberation. Note that Cousine is metrically | |
70 // compatible with Courier New, but the former is sans-serif while | |
71 // the latter is serif. | |
72 | |
73 | |
74 struct FontEquivMap { | |
75 FontEquivClass clazz; | |
76 const char name[40]; | |
77 }; | |
78 | |
79 static const FontEquivMap kFontEquivMap[] = { | |
80 { SANS, "Arial" }, | |
81 { SANS, "Arimo" }, | |
82 { SANS, "Liberation Sans" }, | |
83 | |
84 { SERIF, "Times New Roman" }, | |
85 { SERIF, "Tinos" }, | |
86 { SERIF, "Liberation Serif" }, | |
87 | |
88 { MONO, "Courier New" }, | |
89 { MONO, "Cousine" }, | |
90 { MONO, "Liberation Mono" }, | |
91 | |
92 { SYMBOL, "Symbol" }, | |
93 { SYMBOL, "Symbol Neu" }, | |
94 | |
95 // MS Pゴシック | |
96 { PGOTHIC, "MS PGothic" }, | |
97 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" | |
98 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, | |
99 { PGOTHIC, "IPAPGothic" }, | |
100 { PGOTHIC, "MotoyaG04Gothic" }, | |
101 | |
102 // MS ゴシック | |
103 { GOTHIC, "MS Gothic" }, | |
104 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 " | |
105 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, | |
106 { GOTHIC, "IPAGothic" }, | |
107 { GOTHIC, "MotoyaG04GothicMono" }, | |
108 | |
109 // MS P明朝 | |
110 { PMINCHO, "MS PMincho" }, | |
111 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" | |
112 "\xe6\x98\x8e\xe6\x9c\x9d"}, | |
113 { PMINCHO, "IPAPMincho" }, | |
114 { PMINCHO, "MotoyaG04Mincho" }, | |
115 | |
116 // MS 明朝 | |
117 { MINCHO, "MS Mincho" }, | |
118 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" }, | |
119 { MINCHO, "IPAMincho" }, | |
120 { MINCHO, "MotoyaG04MinchoMono" }, | |
121 | |
122 // 宋体 | |
123 { SIMSUN, "Simsun" }, | |
124 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" }, | |
125 { SIMSUN, "MSung GB18030" }, | |
126 { SIMSUN, "Song ASC" }, | |
127 | |
128 // 新宋体 | |
129 { NSIMSUN, "NSimsun" }, | |
130 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" }, | |
131 { NSIMSUN, "MSung GB18030" }, | |
132 { NSIMSUN, "N Song ASC" }, | |
133 | |
134 // 黑体 | |
135 { SIMHEI, "Simhei" }, | |
136 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" }, | |
137 { SIMHEI, "MYingHeiGB18030" }, | |
138 { SIMHEI, "MYingHeiB5HK" }, | |
139 | |
140 // 新細明體 | |
141 { PMINGLIU, "PMingLiU"}, | |
142 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, | |
143 { PMINGLIU, "MSung B5HK"}, | |
144 | |
145 // 細明體 | |
146 { MINGLIU, "MingLiU"}, | |
147 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, | |
148 { MINGLIU, "MSung B5HK"}, | |
149 | |
150 // 新細明體 | |
151 { PMINGLIUHK, "PMingLiU_HKSCS"}, | |
152 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }
, | |
153 { PMINGLIUHK, "MSung B5HK"}, | |
154 | |
155 // 細明體 | |
156 { MINGLIUHK, "MingLiU_HKSCS"}, | |
157 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, | |
158 { MINGLIUHK, "MSung B5HK"}, | |
159 }; | |
160 | |
161 static const size_t kFontCount = | |
162 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]); | |
163 | |
164 // TODO(jungshik): If this loop turns out to be hot, turn | |
165 // the array to a static (hash)map to speed it up. | |
166 for (size_t i = 0; i < kFontCount; ++i) { | |
167 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0) | |
168 return kFontEquivMap[i].clazz; | |
169 } | |
170 return OTHER; | |
171 } | |
172 | |
173 | |
174 // Return true if |font_a| and |font_b| are visually and at the metrics | |
175 // level interchangeable. | |
176 bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b) | |
177 { | |
178 FontEquivClass class_a = GetFontEquivClass(font_a); | |
179 FontEquivClass class_b = GetFontEquivClass(font_b); | |
180 | |
181 return class_a != OTHER && class_a == class_b; | |
182 } | |
183 | |
184 inline unsigned FileFaceIdToFileId(unsigned filefaceid) | |
185 { | |
186 return filefaceid >> 4; | |
187 } | |
188 | |
189 inline unsigned FileIdAndFaceIndexToFileFaceId(unsigned fileid, int face_index) | |
190 { | |
191 SkASSERT((face_index & 0xfu) == face_index); | |
192 return (fileid << 4) | face_index; | |
193 } | |
194 | |
195 // Normally we only return exactly the font asked for. In last-resort | |
196 // cases, the request either doesn't specify a font or is one of the | |
197 // basic font names like "Sans", "Serif" or "Monospace". This function | |
198 // tells you whether a given request is for such a fallback. | |
199 bool IsFallbackFontAllowed(const std::string& family) { | |
200 const char* family_cstr = family.c_str(); | |
201 return family.empty() || | |
202 strcasecmp(family_cstr, "sans") == 0 || | |
203 strcasecmp(family_cstr, "serif") == 0 || | |
204 strcasecmp(family_cstr, "monospace") == 0; | |
205 } | |
206 | |
207 // Find matching font from |font_set| for the given font family. | |
208 FcPattern* MatchFont(FcFontSet* font_set, | |
209 FcChar8* post_config_family, | |
210 const std::string& family) { | |
211 // Older versions of fontconfig have a bug where they cannot select | |
212 // only scalable fonts so we have to manually filter the results. | |
213 FcPattern* match = NULL; | |
214 for (int i = 0; i < font_set->nfont; ++i) { | |
215 FcPattern* current = font_set->fonts[i]; | |
216 FcBool is_scalable; | |
217 | |
218 if (FcPatternGetBool(current, FC_SCALABLE, 0, | |
219 &is_scalable) != FcResultMatch || | |
220 !is_scalable) { | |
221 continue; | |
222 } | |
223 | |
224 // fontconfig can also return fonts which are unreadable | |
225 FcChar8* c_filename; | |
226 if (FcPatternGetString(current, FC_FILE, 0, &c_filename) != FcResultMatch) | |
227 continue; | |
228 | |
229 if (access(reinterpret_cast<char*>(c_filename), R_OK) != 0) | |
230 continue; | |
231 | |
232 match = current; | |
233 break; | |
234 } | |
235 | |
236 if (match && !IsFallbackFontAllowed(family)) { | |
237 bool acceptable_substitute = false; | |
238 for (int id = 0; id < 255; ++id) { | |
239 FcChar8* post_match_family; | |
240 if (FcPatternGetString(match, FC_FAMILY, id, &post_match_family) != | |
241 FcResultMatch) | |
242 break; | |
243 acceptable_substitute = | |
244 (strcasecmp(reinterpret_cast<char*>(post_config_family), | |
245 reinterpret_cast<char*>(post_match_family)) == 0 || | |
246 // Workaround for Issue 12530: | |
247 // requested family: "Bitstream Vera Sans" | |
248 // post_config_family: "Arial" | |
249 // post_match_family: "Bitstream Vera Sans" | |
250 // -> We should treat this case as a good match. | |
251 strcasecmp(family.c_str(), | |
252 reinterpret_cast<char*>(post_match_family)) == 0) || | |
253 IsMetricCompatibleReplacement(family.c_str(), | |
254 reinterpret_cast<char*>(post_match_family)); | |
255 if (acceptable_substitute) | |
256 break; | |
257 } | |
258 if (!acceptable_substitute) | |
259 return NULL; | |
260 } | |
261 | |
262 return match; | |
263 } | |
264 | |
265 // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|. | |
266 bool GetFontProperties(FcPattern* font, | |
267 std::string* font_family, | |
268 bool* is_bold, | |
269 bool* is_italic) { | |
270 FcChar8* c_family; | |
271 if (FcPatternGetString(font, FC_FAMILY, 0, &c_family)) | |
272 return false; | |
273 | |
274 int resulting_bold; | |
275 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold)) | |
276 resulting_bold = FC_WEIGHT_NORMAL; | |
277 | |
278 int resulting_italic; | |
279 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic)) | |
280 resulting_italic = FC_SLANT_ROMAN; | |
281 | |
282 // If we ask for an italic font, fontconfig might take a roman font and set | |
283 // the undocumented property FC_MATRIX to a skew matrix. It'll then say | |
284 // that the font is italic or oblique. So, if we see a matrix, we don't | |
285 // believe that it's italic. | |
286 FcValue matrix; | |
287 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0; | |
288 | |
289 // If we ask for an italic font, fontconfig might take a roman font and set | |
290 // FC_EMBOLDEN. | |
291 FcValue embolden; | |
292 const bool have_embolden = | |
293 FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0; | |
294 | |
295 *is_bold = resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden; | |
296 *is_italic = resulting_italic > FC_SLANT_ROMAN && !have_matrix; | |
297 *font_family = reinterpret_cast<char*>(c_family); | |
298 | |
299 return true; | |
300 } | |
301 | |
302 } // anonymous namespace | |
303 | |
304 FontConfigDirect::FontConfigDirect() | |
305 : next_file_id_(0) { | |
306 FcInit(); | |
307 } | |
308 | |
309 FontConfigDirect::~FontConfigDirect() { | |
310 } | |
311 | |
312 bool FontConfigDirect::Match(std::string* result_family, | |
313 unsigned* result_filefaceid, | |
314 bool filefaceid_valid, unsigned filefaceid, | |
315 const std::string& family, | |
316 const void* data, size_t characters_bytes, | |
317 bool* is_bold, bool* is_italic) { | |
318 if (family.length() > kMaxFontFamilyLength) | |
319 return false; | |
320 | |
321 SkAutoMutexAcquire ac(mutex_); | |
322 | |
323 // Given |family|, |is_bold| and |is_italic| but not |data|, the result will | |
324 // be a function of these three parameters, and thus eligible for caching. | |
325 // This is the fast path for |SkTypeface::CreateFromName()|. | |
326 bool eligible_for_cache = !family.empty() && is_bold && is_italic && !data; | |
327 if (eligible_for_cache) { | |
328 int style = (*is_bold ? SkTypeface::kBold : 0 ) | | |
329 (*is_italic ? SkTypeface::kItalic : 0); | |
330 FontMatchKey key = FontMatchKey(family, style); | |
331 const std::map<FontMatchKey, FontMatch>::const_iterator i = | |
332 font_match_cache_.find(key); | |
333 if (i != font_match_cache_.end()) { | |
334 *is_bold = i->second.is_bold; | |
335 *is_italic = i->second.is_italic; | |
336 if (result_family) | |
337 *result_family = i->second.family; | |
338 if (result_filefaceid) | |
339 *result_filefaceid = i->second.filefaceid; | |
340 return true; | |
341 } | |
342 } | |
343 | |
344 FcPattern* pattern = FcPatternCreate(); | |
345 | |
346 if (filefaceid_valid) { | |
347 const std::map<unsigned, std::string>::const_iterator | |
348 i = fileid_to_filename_.find(FileFaceIdToFileId(filefaceid)); | |
349 if (i == fileid_to_filename_.end()) { | |
350 FcPatternDestroy(pattern); | |
351 return false; | |
352 } | |
353 int face_index = filefaceid & 0xfu; | |
354 FcPatternAddString(pattern, FC_FILE, | |
355 reinterpret_cast<const FcChar8*>(i->second.c_str())); | |
356 // face_index is added only when family is empty because it is not | |
357 // necessary to uniquiely identify a font if both file and | |
358 // family are given. | |
359 if (family.empty()) | |
360 FcPatternAddInteger(pattern, FC_INDEX, face_index); | |
361 } | |
362 if (!family.empty()) { | |
363 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) family.c_str()); | |
364 } | |
365 | |
366 FcCharSet* charset = NULL; | |
367 if (data) { | |
368 charset = FcCharSetCreate(); | |
369 const uint16_t* chars = (const uint16_t*)data; | |
370 const uint16_t* stop = chars + characters_bytes/2; | |
371 while (chars < stop) { | |
372 FcCharSetAddChar(charset, SkUTF16_NextUnichar(&chars)); | |
373 } | |
374 FcPatternAddCharSet(pattern, FC_CHARSET, charset); | |
375 FcCharSetDestroy(charset); // pattern now owns it. | |
376 } | |
377 | |
378 FcPatternAddInteger(pattern, FC_WEIGHT, | |
379 is_bold && *is_bold ? FC_WEIGHT_BOLD | |
380 : FC_WEIGHT_NORMAL); | |
381 FcPatternAddInteger(pattern, FC_SLANT, | |
382 is_italic && *is_italic ? FC_SLANT_ITALIC | |
383 : FC_SLANT_ROMAN); | |
384 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); | |
385 | |
386 FcConfigSubstitute(NULL, pattern, FcMatchPattern); | |
387 FcDefaultSubstitute(pattern); | |
388 | |
389 // Font matching: | |
390 // CSS often specifies a fallback list of families: | |
391 // font-family: a, b, c, serif; | |
392 // However, fontconfig will always do its best to find *a* font when asked | |
393 // for something so we need a way to tell if the match which it has found is | |
394 // "good enough" for us. Otherwise, we can return NULL which gets piped up | |
395 // and lets WebKit know to try the next CSS family name. However, fontconfig | |
396 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we | |
397 // wish to support that. | |
398 // | |
399 // Thus, if a specific family is requested we set @family_requested. Then we | |
400 // record two strings: the family name after config processing and the | |
401 // family name after resolving. If the two are equal, it's a good match. | |
402 // | |
403 // So consider the case where a user has mapped Arial to Helvetica in their | |
404 // config. | |
405 // requested family: "Arial" | |
406 // post_config_family: "Helvetica" | |
407 // post_match_family: "Helvetica" | |
408 // -> good match | |
409 // | |
410 // and for a missing font: | |
411 // requested family: "Monaco" | |
412 // post_config_family: "Monaco" | |
413 // post_match_family: "Times New Roman" | |
414 // -> BAD match | |
415 // | |
416 // However, we special-case fallback fonts; see IsFallbackFontAllowed(). | |
417 FcChar8* post_config_family; | |
418 FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family); | |
419 | |
420 FcResult result; | |
421 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); | |
422 if (!font_set) { | |
423 FcPatternDestroy(pattern); | |
424 return false; | |
425 } | |
426 | |
427 FcPattern* match = MatchFont(font_set, post_config_family, family); | |
428 if (!match) { | |
429 FcPatternDestroy(pattern); | |
430 FcFontSetDestroy(font_set); | |
431 return false; | |
432 } | |
433 | |
434 FcPatternDestroy(pattern); | |
435 | |
436 FcChar8* c_filename; | |
437 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) { | |
438 FcFontSetDestroy(font_set); | |
439 return false; | |
440 } | |
441 int face_index; | |
442 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { | |
443 FcFontSetDestroy(font_set); | |
444 return false; | |
445 } | |
446 | |
447 FontMatch font_match; | |
448 if (filefaceid_valid) { | |
449 font_match.filefaceid = filefaceid; | |
450 } else { | |
451 unsigned out_fileid; | |
452 const std::string filename(reinterpret_cast<char*>(c_filename)); | |
453 const std::map<std::string, unsigned>::const_iterator | |
454 i = filename_to_fileid_.find(filename); | |
455 if (i == filename_to_fileid_.end()) { | |
456 out_fileid = next_file_id_++; | |
457 filename_to_fileid_[filename] = out_fileid; | |
458 fileid_to_filename_[out_fileid] = filename; | |
459 } else { | |
460 out_fileid = i->second; | |
461 } | |
462 // fileid stored in filename_to_fileid_ and fileid_to_filename_ is | |
463 // unique only up to the font file. We have to encode face_index for | |
464 // the out param. | |
465 font_match.filefaceid = | |
466 FileIdAndFaceIndexToFileFaceId(out_fileid, face_index); | |
467 } | |
468 | |
469 bool success = GetFontProperties(match, | |
470 &font_match.family, | |
471 &font_match.is_bold, | |
472 &font_match.is_italic); | |
473 FcFontSetDestroy(font_set); | |
474 | |
475 if (success) { | |
476 // If eligible, cache the result of the matching. | |
477 if (eligible_for_cache) { | |
478 int style = (*is_bold ? SkTypeface::kBold : 0 ) | | |
479 (*is_italic ? SkTypeface::kItalic : 0); | |
480 font_match_cache_[FontMatchKey(family, style)] = font_match; | |
481 } | |
482 | |
483 if (result_family) | |
484 *result_family = font_match.family; | |
485 if (result_filefaceid) | |
486 *result_filefaceid = font_match.filefaceid; | |
487 if (is_bold) | |
488 *is_bold = font_match.is_bold; | |
489 if (is_italic) | |
490 *is_italic = font_match.is_italic; | |
491 } | |
492 | |
493 return success; | |
494 } | |
495 | |
496 int FontConfigDirect::Open(unsigned filefaceid) { | |
497 SkAutoMutexAcquire ac(mutex_); | |
498 const std::map<unsigned, std::string>::const_iterator | |
499 i = fileid_to_filename_.find(FileFaceIdToFileId(filefaceid)); | |
500 if (i == fileid_to_filename_.end()) | |
501 return -1; | |
502 | |
503 return open(i->second.c_str(), O_RDONLY); | |
504 } | |
OLD | NEW |