OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2009 Google Inc. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 /* migrated from chrome/src/skia/ext/SkFontHost_fontconfig_direct.cpp */ | |
9 | |
10 #include "SkBuffer.h" | |
11 #include "SkDataTable.h" | |
12 #include "SkFontConfigInterface.h" | |
13 #include "SkFontStyle.h" | |
14 #include "SkMutex.h" | |
15 #include "SkStream.h" | |
16 #include "SkString.h" | |
17 #include "SkTArray.h" | |
18 #include "SkTDArray.h" | |
19 #include "SkTemplates.h" | |
20 #include "SkTypeface.h" | |
21 #include "SkTypes.h" | |
22 | |
23 #include <fontconfig/fontconfig.h> | |
24 #include <unistd.h> | |
25 | |
26 size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const { | |
27 size_t size = sizeof(fID) + sizeof(fTTCIndex); | |
28 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, widt
h, italic | |
29 size += sizeof(int32_t) + fString.size(); // store length+data | |
30 if (addr) { | |
31 SkWBuffer buffer(addr, size); | |
32 | |
33 buffer.write32(fID); | |
34 buffer.write32(fTTCIndex); | |
35 buffer.write32(fString.size()); | |
36 buffer.write32(fStyle.weight()); | |
37 buffer.write32(fStyle.width()); | |
38 buffer.write8(fStyle.slant()); | |
39 buffer.write(fString.c_str(), fString.size()); | |
40 buffer.padToAlign4(); | |
41 | |
42 SkASSERT(buffer.pos() == size); | |
43 } | |
44 return size; | |
45 } | |
46 | |
47 size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr, | |
48 size_t size) { | |
49 SkRBuffer buffer(addr, size); | |
50 | |
51 (void)buffer.readU32(&fID); | |
52 (void)buffer.readS32(&fTTCIndex); | |
53 uint32_t strLen, weight, width; | |
54 (void)buffer.readU32(&strLen); | |
55 (void)buffer.readU32(&weight); | |
56 (void)buffer.readU32(&width); | |
57 uint8_t u8; | |
58 (void)buffer.readU8(&u8); | |
59 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8; | |
60 fStyle = SkFontStyle(weight, width, slant); | |
61 fString.resize(strLen); | |
62 (void)buffer.read(fString.writable_str(), strLen); | |
63 buffer.skipToAlign4(); | |
64 | |
65 return buffer.pos(); // the actual number of bytes read | |
66 } | |
67 | |
68 #ifdef SK_DEBUG | |
69 static void make_iden(SkFontConfigInterface::FontIdentity* iden) { | |
70 iden->fID = 10; | |
71 iden->fTTCIndex = 2; | |
72 iden->fString.set("Hello world"); | |
73 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant); | |
74 } | |
75 | |
76 static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0, | |
77 int initValue) { | |
78 SkFontConfigInterface::FontIdentity iden1; | |
79 | |
80 size_t size0 = iden0.writeToMemory(nullptr); | |
81 | |
82 SkAutoMalloc storage(size0); | |
83 memset(storage.get(), initValue, size0); | |
84 | |
85 size_t size1 = iden0.writeToMemory(storage.get()); | |
86 SkASSERT(size0 == size1); | |
87 | |
88 SkASSERT(iden0 != iden1); | |
89 size_t size2 = iden1.readFromMemory(storage.get(), size1); | |
90 SkASSERT(size2 == size1); | |
91 SkASSERT(iden0 == iden1); | |
92 } | |
93 | |
94 static void fontconfiginterface_unittest() { | |
95 SkFontConfigInterface::FontIdentity iden0, iden1; | |
96 | |
97 SkASSERT(iden0 == iden1); | |
98 | |
99 make_iden(&iden0); | |
100 SkASSERT(iden0 != iden1); | |
101 | |
102 make_iden(&iden1); | |
103 SkASSERT(iden0 == iden1); | |
104 | |
105 test_writeToMemory(iden0, 0); | |
106 test_writeToMemory(iden0, 0); | |
107 } | |
108 #endif | |
109 | |
110 class SkFontConfigInterfaceDirect : public SkFontConfigInterface { | |
111 public: | |
112 SkFontConfigInterfaceDirect(); | |
113 virtual ~SkFontConfigInterfaceDirect(); | |
114 | |
115 virtual bool matchFamilyName(const char familyName[], | |
116 SkTypeface::Style requested, | |
117 FontIdentity* outFontIdentifier, | |
118 SkString* outFamilyName, | |
119 SkTypeface::Style* outStyle) override; | |
120 SkStreamAsset* openStream(const FontIdentity&) override; | |
121 | |
122 // new APIs | |
123 SkDataTable* getFamilyNames() override; | |
124 virtual bool matchFamilySet(const char inFamilyName[], | |
125 SkString* outFamilyName, | |
126 SkTArray<FontIdentity>*) override; | |
127 | |
128 private: | |
129 SkMutex mutex_; | |
130 }; | |
131 | |
132 SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface(SkBase
Mutex* mutex) { | |
133 SkAutoMutexAcquire ac(mutex); | |
134 static SkFontConfigInterfaceDirect* singleton = nullptr; | |
135 if (singleton == nullptr) { | |
136 singleton = new SkFontConfigInterfaceDirect; | |
137 } | |
138 return singleton; | |
139 } | |
140 | |
141 /////////////////////////////////////////////////////////////////////////////// | |
142 | |
143 // Returns the string from the pattern, or nullptr | |
144 static const char* get_name(FcPattern* pattern, const char field[], | |
145 int index = 0) { | |
146 const char* name; | |
147 if (FcPatternGetString(pattern, field, index, | |
148 (FcChar8**)&name) != FcResultMatch) { | |
149 name = nullptr; | |
150 } | |
151 return name; | |
152 } | |
153 | |
154 /////////////////////////////////////////////////////////////////////////////// | |
155 | |
156 namespace { | |
157 | |
158 // Equivalence classes, used to match the Liberation and other fonts | |
159 // with their metric-compatible replacements. See the discussion in | |
160 // GetFontEquivClass(). | |
161 enum FontEquivClass | |
162 { | |
163 OTHER, | |
164 SANS, | |
165 SERIF, | |
166 MONO, | |
167 SYMBOL, | |
168 PGOTHIC, | |
169 GOTHIC, | |
170 PMINCHO, | |
171 MINCHO, | |
172 SIMSUN, | |
173 NSIMSUN, | |
174 SIMHEI, | |
175 PMINGLIU, | |
176 MINGLIU, | |
177 PMINGLIUHK, | |
178 MINGLIUHK, | |
179 CAMBRIA, | |
180 CALIBRI, | |
181 }; | |
182 | |
183 // Match the font name against a whilelist of fonts, returning the equivalence | |
184 // class. | |
185 FontEquivClass GetFontEquivClass(const char* fontname) | |
186 { | |
187 // It would be nice for fontconfig to tell us whether a given suggested | |
188 // replacement is a "strong" match (that is, an equivalent font) or | |
189 // a "weak" match (that is, fontconfig's next-best attempt at finding a | |
190 // substitute). However, I played around with the fontconfig API for | |
191 // a good few hours and could not make it reveal this information. | |
192 // | |
193 // So instead, we hardcode. Initially this function emulated | |
194 // /etc/fonts/conf.d/30-metric-aliases.conf | |
195 // from my Ubuntu system, but we're better off being very conservative. | |
196 | |
197 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with | |
198 // Arial, Times New Roman and Courier New with a character repertoire | |
199 // much larger than Liberation. Note that Cousine is metrically | |
200 // compatible with Courier New, but the former is sans-serif while | |
201 // the latter is serif. | |
202 | |
203 | |
204 struct FontEquivMap { | |
205 FontEquivClass clazz; | |
206 const char name[40]; | |
207 }; | |
208 | |
209 static const FontEquivMap kFontEquivMap[] = { | |
210 { SANS, "Arial" }, | |
211 { SANS, "Arimo" }, | |
212 { SANS, "Liberation Sans" }, | |
213 | |
214 { SERIF, "Times New Roman" }, | |
215 { SERIF, "Tinos" }, | |
216 { SERIF, "Liberation Serif" }, | |
217 | |
218 { MONO, "Courier New" }, | |
219 { MONO, "Cousine" }, | |
220 { MONO, "Liberation Mono" }, | |
221 | |
222 { SYMBOL, "Symbol" }, | |
223 { SYMBOL, "Symbol Neu" }, | |
224 | |
225 // MS Pゴシック | |
226 { PGOTHIC, "MS PGothic" }, | |
227 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" | |
228 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, | |
229 { PGOTHIC, "Noto Sans CJK JP" }, | |
230 { PGOTHIC, "IPAPGothic" }, | |
231 { PGOTHIC, "MotoyaG04Gothic" }, | |
232 | |
233 // MS ゴシック | |
234 { GOTHIC, "MS Gothic" }, | |
235 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 " | |
236 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, | |
237 { GOTHIC, "Noto Sans Mono CJK JP" }, | |
238 { GOTHIC, "IPAGothic" }, | |
239 { GOTHIC, "MotoyaG04GothicMono" }, | |
240 | |
241 // MS P明朝 | |
242 { PMINCHO, "MS PMincho" }, | |
243 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" | |
244 "\xe6\x98\x8e\xe6\x9c\x9d"}, | |
245 { PMINCHO, "IPAPMincho" }, | |
246 { PMINCHO, "MotoyaG04Mincho" }, | |
247 | |
248 // MS 明朝 | |
249 { MINCHO, "MS Mincho" }, | |
250 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" }, | |
251 { MINCHO, "IPAMincho" }, | |
252 { MINCHO, "MotoyaG04MinchoMono" }, | |
253 | |
254 // 宋体 | |
255 { SIMSUN, "Simsun" }, | |
256 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" }, | |
257 { SIMSUN, "MSung GB18030" }, | |
258 { SIMSUN, "Song ASC" }, | |
259 | |
260 // 新宋体 | |
261 { NSIMSUN, "NSimsun" }, | |
262 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" }, | |
263 { NSIMSUN, "MSung GB18030" }, | |
264 { NSIMSUN, "N Song ASC" }, | |
265 | |
266 // 黑体 | |
267 { SIMHEI, "Simhei" }, | |
268 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" }, | |
269 { SIMHEI, "Noto Sans CJK SC" }, | |
270 { SIMHEI, "MYingHeiGB18030" }, | |
271 { SIMHEI, "MYingHeiB5HK" }, | |
272 | |
273 // 新細明體 | |
274 { PMINGLIU, "PMingLiU"}, | |
275 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, | |
276 { PMINGLIU, "MSung B5HK"}, | |
277 | |
278 // 細明體 | |
279 { MINGLIU, "MingLiU"}, | |
280 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, | |
281 { MINGLIU, "MSung B5HK"}, | |
282 | |
283 // 新細明體 | |
284 { PMINGLIUHK, "PMingLiU_HKSCS"}, | |
285 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }
, | |
286 { PMINGLIUHK, "MSung B5HK"}, | |
287 | |
288 // 細明體 | |
289 { MINGLIUHK, "MingLiU_HKSCS"}, | |
290 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, | |
291 { MINGLIUHK, "MSung B5HK"}, | |
292 | |
293 // Cambria | |
294 { CAMBRIA, "Cambria" }, | |
295 { CAMBRIA, "Caladea" }, | |
296 | |
297 // Calibri | |
298 { CALIBRI, "Calibri" }, | |
299 { CALIBRI, "Carlito" }, | |
300 }; | |
301 | |
302 static const size_t kFontCount = | |
303 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]); | |
304 | |
305 // TODO(jungshik): If this loop turns out to be hot, turn | |
306 // the array to a static (hash)map to speed it up. | |
307 for (size_t i = 0; i < kFontCount; ++i) { | |
308 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0) | |
309 return kFontEquivMap[i].clazz; | |
310 } | |
311 return OTHER; | |
312 } | |
313 | |
314 | |
315 // Return true if |font_a| and |font_b| are visually and at the metrics | |
316 // level interchangeable. | |
317 bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b) | |
318 { | |
319 FontEquivClass class_a = GetFontEquivClass(font_a); | |
320 FontEquivClass class_b = GetFontEquivClass(font_b); | |
321 | |
322 return class_a != OTHER && class_a == class_b; | |
323 } | |
324 | |
325 // Normally we only return exactly the font asked for. In last-resort | |
326 // cases, the request either doesn't specify a font or is one of the | |
327 // basic font names like "Sans", "Serif" or "Monospace". This function | |
328 // tells you whether a given request is for such a fallback. | |
329 bool IsFallbackFontAllowed(const SkString& family) { | |
330 const char* family_cstr = family.c_str(); | |
331 return family.isEmpty() || | |
332 strcasecmp(family_cstr, "sans") == 0 || | |
333 strcasecmp(family_cstr, "serif") == 0 || | |
334 strcasecmp(family_cstr, "monospace") == 0; | |
335 } | |
336 | |
337 static bool valid_pattern(FcPattern* pattern) { | |
338 #ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS | |
339 FcBool is_scalable; | |
340 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch | |
341 || !is_scalable) { | |
342 return false; | |
343 } | |
344 #endif | |
345 | |
346 // fontconfig can also return fonts which are unreadable | |
347 const char* c_filename = get_name(pattern, FC_FILE); | |
348 if (!c_filename) { | |
349 return false; | |
350 } | |
351 if (access(c_filename, R_OK) != 0) { | |
352 return false; | |
353 } | |
354 return true; | |
355 } | |
356 | |
357 // Find matching font from |font_set| for the given font family. | |
358 FcPattern* MatchFont(FcFontSet* font_set, | |
359 const char* post_config_family, | |
360 const SkString& family) { | |
361 // Older versions of fontconfig have a bug where they cannot select | |
362 // only scalable fonts so we have to manually filter the results. | |
363 FcPattern* match = nullptr; | |
364 for (int i = 0; i < font_set->nfont; ++i) { | |
365 FcPattern* current = font_set->fonts[i]; | |
366 if (valid_pattern(current)) { | |
367 match = current; | |
368 break; | |
369 } | |
370 } | |
371 | |
372 if (match && !IsFallbackFontAllowed(family)) { | |
373 bool acceptable_substitute = false; | |
374 for (int id = 0; id < 255; ++id) { | |
375 const char* post_match_family = get_name(match, FC_FAMILY, id); | |
376 if (!post_match_family) | |
377 break; | |
378 acceptable_substitute = | |
379 (strcasecmp(post_config_family, post_match_family) == 0 || | |
380 // Workaround for Issue 12530: | |
381 // requested family: "Bitstream Vera Sans" | |
382 // post_config_family: "Arial" | |
383 // post_match_family: "Bitstream Vera Sans" | |
384 // -> We should treat this case as a good match. | |
385 strcasecmp(family.c_str(), post_match_family) == 0) || | |
386 IsMetricCompatibleReplacement(family.c_str(), post_match_family); | |
387 if (acceptable_substitute) | |
388 break; | |
389 } | |
390 if (!acceptable_substitute) | |
391 return nullptr; | |
392 } | |
393 | |
394 return match; | |
395 } | |
396 | |
397 // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|. | |
398 SkTypeface::Style GetFontStyle(FcPattern* font) { | |
399 int resulting_bold; | |
400 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold)) | |
401 resulting_bold = FC_WEIGHT_NORMAL; | |
402 | |
403 int resulting_italic; | |
404 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic)) | |
405 resulting_italic = FC_SLANT_ROMAN; | |
406 | |
407 // If we ask for an italic font, fontconfig might take a roman font and set | |
408 // the undocumented property FC_MATRIX to a skew matrix. It'll then say | |
409 // that the font is italic or oblique. So, if we see a matrix, we don't | |
410 // believe that it's italic. | |
411 FcValue matrix; | |
412 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0; | |
413 | |
414 // If we ask for an italic font, fontconfig might take a roman font and set | |
415 // FC_EMBOLDEN. | |
416 FcValue embolden; | |
417 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) ==
0; | |
418 | |
419 int styleBits = 0; | |
420 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) { | |
421 styleBits |= SkTypeface::kBold; | |
422 } | |
423 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) { | |
424 styleBits |= SkTypeface::kItalic; | |
425 } | |
426 | |
427 return (SkTypeface::Style)styleBits; | |
428 } | |
429 | |
430 } // anonymous namespace | |
431 | |
432 /////////////////////////////////////////////////////////////////////////////// | |
433 | |
434 #define kMaxFontFamilyLength 2048 | |
435 | |
436 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() { | |
437 SkAutoMutexAcquire ac(mutex_); | |
438 | |
439 FcInit(); | |
440 | |
441 SkDEBUGCODE(fontconfiginterface_unittest();) | |
442 } | |
443 | |
444 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() { | |
445 } | |
446 | |
447 bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[], | |
448 SkTypeface::Style style, | |
449 FontIdentity* outIdentity, | |
450 SkString* outFamilyName, | |
451 SkTypeface::Style* outStyle) { | |
452 SkString familyStr(familyName ? familyName : ""); | |
453 if (familyStr.size() > kMaxFontFamilyLength) { | |
454 return false; | |
455 } | |
456 | |
457 SkAutoMutexAcquire ac(mutex_); | |
458 | |
459 FcPattern* pattern = FcPatternCreate(); | |
460 | |
461 if (familyName) { | |
462 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); | |
463 } | |
464 FcPatternAddInteger(pattern, FC_WEIGHT, | |
465 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD | |
466 : FC_WEIGHT_NORMAL); | |
467 FcPatternAddInteger(pattern, FC_SLANT, | |
468 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC | |
469 : FC_SLANT_ROMAN); | |
470 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); | |
471 | |
472 FcConfigSubstitute(nullptr, pattern, FcMatchPattern); | |
473 FcDefaultSubstitute(pattern); | |
474 | |
475 // Font matching: | |
476 // CSS often specifies a fallback list of families: | |
477 // font-family: a, b, c, serif; | |
478 // However, fontconfig will always do its best to find *a* font when asked | |
479 // for something so we need a way to tell if the match which it has found is | |
480 // "good enough" for us. Otherwise, we can return nullptr which gets piped u
p | |
481 // and lets WebKit know to try the next CSS family name. However, fontconfig | |
482 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we | |
483 // wish to support that. | |
484 // | |
485 // Thus, if a specific family is requested we set @family_requested. Then we | |
486 // record two strings: the family name after config processing and the | |
487 // family name after resolving. If the two are equal, it's a good match. | |
488 // | |
489 // So consider the case where a user has mapped Arial to Helvetica in their | |
490 // config. | |
491 // requested family: "Arial" | |
492 // post_config_family: "Helvetica" | |
493 // post_match_family: "Helvetica" | |
494 // -> good match | |
495 // | |
496 // and for a missing font: | |
497 // requested family: "Monaco" | |
498 // post_config_family: "Monaco" | |
499 // post_match_family: "Times New Roman" | |
500 // -> BAD match | |
501 // | |
502 // However, we special-case fallback fonts; see IsFallbackFontAllowed(). | |
503 | |
504 const char* post_config_family = get_name(pattern, FC_FAMILY); | |
505 if (!post_config_family) { | |
506 // we can just continue with an empty name, e.g. default font | |
507 post_config_family = ""; | |
508 } | |
509 | |
510 FcResult result; | |
511 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); | |
512 if (!font_set) { | |
513 FcPatternDestroy(pattern); | |
514 return false; | |
515 } | |
516 | |
517 FcPattern* match = MatchFont(font_set, post_config_family, familyStr); | |
518 if (!match) { | |
519 FcPatternDestroy(pattern); | |
520 FcFontSetDestroy(font_set); | |
521 return false; | |
522 } | |
523 | |
524 FcPatternDestroy(pattern); | |
525 | |
526 // From here out we just extract our results from 'match' | |
527 | |
528 post_config_family = get_name(match, FC_FAMILY); | |
529 if (!post_config_family) { | |
530 FcFontSetDestroy(font_set); | |
531 return false; | |
532 } | |
533 | |
534 const char* c_filename = get_name(match, FC_FILE); | |
535 if (!c_filename) { | |
536 FcFontSetDestroy(font_set); | |
537 return false; | |
538 } | |
539 | |
540 int face_index; | |
541 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { | |
542 FcFontSetDestroy(font_set); | |
543 return false; | |
544 } | |
545 | |
546 FcFontSetDestroy(font_set); | |
547 | |
548 if (outIdentity) { | |
549 outIdentity->fTTCIndex = face_index; | |
550 outIdentity->fString.set(c_filename); | |
551 } | |
552 if (outFamilyName) { | |
553 outFamilyName->set(post_config_family); | |
554 } | |
555 if (outStyle) { | |
556 *outStyle = GetFontStyle(match); | |
557 } | |
558 return true; | |
559 } | |
560 | |
561 SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& ident
ity) { | |
562 return SkStream::NewFromFile(identity.fString.c_str()); | |
563 } | |
564 | |
565 /////////////////////////////////////////////////////////////////////////////// | |
566 | |
567 static bool find_name(const SkTDArray<const char*>& list, const char* str) { | |
568 int count = list.count(); | |
569 for (int i = 0; i < count; ++i) { | |
570 if (!strcmp(list[i], str)) { | |
571 return true; | |
572 } | |
573 } | |
574 return false; | |
575 } | |
576 | |
577 SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() { | |
578 SkAutoMutexAcquire ac(mutex_); | |
579 | |
580 FcPattern* pat = FcPatternCreate(); | |
581 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat); | |
582 if (nullptr == pat) { | |
583 return nullptr; | |
584 } | |
585 | |
586 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0); | |
587 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os); | |
588 if (nullptr == os) { | |
589 return nullptr; | |
590 } | |
591 | |
592 FcFontSet* fs = FcFontList(nullptr, pat, os); | |
593 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs); | |
594 if (nullptr == fs) { | |
595 return nullptr; | |
596 } | |
597 | |
598 SkTDArray<const char*> names; | |
599 SkTDArray<size_t> sizes; | |
600 for (int i = 0; i < fs->nfont; ++i) { | |
601 FcPattern* match = fs->fonts[i]; | |
602 const char* famName = get_name(match, FC_FAMILY); | |
603 if (famName && !find_name(names, famName)) { | |
604 *names.append() = famName; | |
605 *sizes.append() = strlen(famName) + 1; | |
606 } | |
607 } | |
608 | |
609 return SkDataTable::NewCopyArrays((const void*const*)names.begin(), | |
610 sizes.begin(), names.count()); | |
611 } | |
612 | |
613 bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[], | |
614 SkString* outFamilyName, | |
615 SkTArray<FontIdentity>* ids) { | |
616 SkAutoMutexAcquire ac(mutex_); | |
617 | |
618 #if 0 | |
619 SkString familyStr(familyName ? familyName : ""); | |
620 if (familyStr.size() > kMaxFontFamilyLength) { | |
621 return false; | |
622 } | |
623 | |
624 SkAutoMutexAcquire ac(mutex_); | |
625 | |
626 FcPattern* pattern = FcPatternCreate(); | |
627 | |
628 if (familyName) { | |
629 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); | |
630 } | |
631 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); | |
632 | |
633 FcConfigSubstitute(nullptr, pattern, FcMatchPattern); | |
634 FcDefaultSubstitute(pattern); | |
635 | |
636 // Font matching: | |
637 // CSS often specifies a fallback list of families: | |
638 // font-family: a, b, c, serif; | |
639 // However, fontconfig will always do its best to find *a* font when asked | |
640 // for something so we need a way to tell if the match which it has found is | |
641 // "good enough" for us. Otherwise, we can return nullptr which gets piped u
p | |
642 // and lets WebKit know to try the next CSS family name. However, fontconfig | |
643 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we | |
644 // wish to support that. | |
645 // | |
646 // Thus, if a specific family is requested we set @family_requested. Then we | |
647 // record two strings: the family name after config processing and the | |
648 // family name after resolving. If the two are equal, it's a good match. | |
649 // | |
650 // So consider the case where a user has mapped Arial to Helvetica in their | |
651 // config. | |
652 // requested family: "Arial" | |
653 // post_config_family: "Helvetica" | |
654 // post_match_family: "Helvetica" | |
655 // -> good match | |
656 // | |
657 // and for a missing font: | |
658 // requested family: "Monaco" | |
659 // post_config_family: "Monaco" | |
660 // post_match_family: "Times New Roman" | |
661 // -> BAD match | |
662 // | |
663 // However, we special-case fallback fonts; see IsFallbackFontAllowed(). | |
664 | |
665 const char* post_config_family = get_name(pattern, FC_FAMILY); | |
666 | |
667 FcResult result; | |
668 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); | |
669 if (!font_set) { | |
670 FcPatternDestroy(pattern); | |
671 return false; | |
672 } | |
673 | |
674 FcPattern* match = MatchFont(font_set, post_config_family, familyStr); | |
675 if (!match) { | |
676 FcPatternDestroy(pattern); | |
677 FcFontSetDestroy(font_set); | |
678 return false; | |
679 } | |
680 | |
681 FcPatternDestroy(pattern); | |
682 | |
683 // From here out we just extract our results from 'match' | |
684 | |
685 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResult
Match) { | |
686 FcFontSetDestroy(font_set); | |
687 return false; | |
688 } | |
689 | |
690 FcChar8* c_filename; | |
691 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) { | |
692 FcFontSetDestroy(font_set); | |
693 return false; | |
694 } | |
695 | |
696 int face_index; | |
697 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { | |
698 FcFontSetDestroy(font_set); | |
699 return false; | |
700 } | |
701 | |
702 FcFontSetDestroy(font_set); | |
703 | |
704 if (outIdentity) { | |
705 outIdentity->fTTCIndex = face_index; | |
706 outIdentity->fString.set((const char*)c_filename); | |
707 } | |
708 if (outFamilyName) { | |
709 outFamilyName->set((const char*)post_config_family); | |
710 } | |
711 if (outStyle) { | |
712 *outStyle = GetFontStyle(match); | |
713 } | |
714 return true; | |
715 | |
716 //////////////////// | |
717 | |
718 int count; | |
719 FcPattern** match = MatchFont(font_set, post_config_family, &count); | |
720 if (!match) { | |
721 FcPatternDestroy(pattern); | |
722 FcFontSetDestroy(font_set); | |
723 return nullptr; | |
724 } | |
725 | |
726 FcPatternDestroy(pattern); | |
727 | |
728 SkTDArray<FcPattern*> trimmedMatches; | |
729 for (int i = 0; i < count; ++i) { | |
730 const char* justName = find_just_name(get_name(match[i], FC_FILE)); | |
731 if (!is_lower(*justName)) { | |
732 *trimmedMatches.append() = match[i]; | |
733 } | |
734 } | |
735 | |
736 SkFontStyleSet_FC* sset = new SkFontStyleSet_FC
(trimmedMatches.begin(),
trimmedMatches.count()); | |
737 #endif | |
738 return false; | |
739 } | |
OLD | NEW |