blob: f1ac7342dc925268422c66babc79817bfd060534 [file] [log] [blame]
reed@google.comb1c65b62013-02-26 15:50:51 +00001/*
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
reed@google.comb1c65b62013-02-26 15:50:51 +000010#include <string>
11#include <unistd.h>
12#include <fcntl.h>
13
14#include <fontconfig/fontconfig.h>
15
reed@google.comf55061f2013-04-22 18:48:45 +000016#include "SkBuffer.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000017#include "SkFontConfigInterface.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000018#include "SkStream.h"
19
reed@google.comf55061f2013-04-22 18:48:45 +000020size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
21 size_t size = sizeof(fID) + sizeof(fTTCIndex);
22 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
23 size += sizeof(int32_t) + fString.size(); // store length+data
24 if (addr) {
25 SkWBuffer buffer(addr, size);
26
27 buffer.write32(fID);
28 buffer.write32(fTTCIndex);
29 buffer.write32(fString.size());
30 buffer.write32(fStyle.weight());
31 buffer.write32(fStyle.width());
32 buffer.write8(fStyle.slant());
33 buffer.write(fString.c_str(), fString.size());
34 buffer.padToAlign4();
35
36 SkASSERT(buffer.pos() == size);
37 }
38 return size;
39}
40
41size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
42 size_t size) {
43 SkRBuffer buffer(addr, size);
44
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000045 (void)buffer.readU32(&fID);
46 (void)buffer.readS32(&fTTCIndex);
47 uint32_t strLen, weight, width;
48 (void)buffer.readU32(&strLen);
49 (void)buffer.readU32(&weight);
50 (void)buffer.readU32(&width);
51 uint8_t u8;
52 (void)buffer.readU8(&u8);
53 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
reed@google.comf55061f2013-04-22 18:48:45 +000054 fStyle = SkFontStyle(weight, width, slant);
55 fString.resize(strLen);
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000056 (void)buffer.read(fString.writable_str(), strLen);
reed@google.comf55061f2013-04-22 18:48:45 +000057 buffer.skipToAlign4();
58
59 return buffer.pos(); // the actual number of bytes read
60}
61
62#ifdef SK_DEBUG
63static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
64 iden->fID = 10;
65 iden->fTTCIndex = 2;
66 iden->fString.set("Hello world");
67 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
68}
69
70static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
71 int initValue) {
72 SkFontConfigInterface::FontIdentity iden1;
73
74 size_t size0 = iden0.writeToMemory(NULL);
75
76 SkAutoMalloc storage(size0);
77 memset(storage.get(), initValue, size0);
78
79 size_t size1 = iden0.writeToMemory(storage.get());
80 SkASSERT(size0 == size1);
81
82 SkASSERT(iden0 != iden1);
83 size_t size2 = iden1.readFromMemory(storage.get(), size1);
84 SkASSERT(size2 == size1);
85 SkASSERT(iden0 == iden1);
86}
87
88static void fontconfiginterface_unittest() {
89 SkFontConfigInterface::FontIdentity iden0, iden1;
90
91 SkASSERT(iden0 == iden1);
92
93 make_iden(&iden0);
94 SkASSERT(iden0 != iden1);
95
96 make_iden(&iden1);
97 SkASSERT(iden0 == iden1);
98
99 test_writeToMemory(iden0, 0);
100 test_writeToMemory(iden0, 0);
101}
102#endif
103
reed@google.comb1c65b62013-02-26 15:50:51 +0000104class SkFontConfigInterfaceDirect : public SkFontConfigInterface {
105public:
106 SkFontConfigInterfaceDirect();
107 virtual ~SkFontConfigInterfaceDirect();
108
reed@google.comf71a2332013-02-27 19:06:30 +0000109 virtual bool matchFamilyName(const char familyName[],
110 SkTypeface::Style requested,
111 FontIdentity* outFontIdentifier,
112 SkString* outFamilyName,
113 SkTypeface::Style* outStyle) SK_OVERRIDE;
114 virtual SkStream* openStream(const FontIdentity&) SK_OVERRIDE;
reed@google.comb1c65b62013-02-26 15:50:51 +0000115
reed@google.com027fd202013-04-19 20:45:30 +0000116 // new APIs
117 virtual SkDataTable* getFamilyNames() SK_OVERRIDE;
118 virtual bool matchFamilySet(const char inFamilyName[],
119 SkString* outFamilyName,
120 SkTArray<FontIdentity>*) SK_OVERRIDE;
121
reed@google.comb1c65b62013-02-26 15:50:51 +0000122private:
123 SkMutex mutex_;
reed@google.comb1c65b62013-02-26 15:50:51 +0000124};
125
reed@google.comd66045e2013-03-04 19:07:02 +0000126SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface() {
127 static SkFontConfigInterface* gDirect;
128 if (NULL == gDirect) {
reed@google.com750a24b2013-04-22 18:45:12 +0000129 static SkMutex gMutex;
130 SkAutoMutexAcquire ac(gMutex);
131
132 if (NULL == gDirect) {
133 gDirect = new SkFontConfigInterfaceDirect;
134 }
reed@google.comd66045e2013-03-04 19:07:02 +0000135 }
136 return gDirect;
reed@google.comb1c65b62013-02-26 15:50:51 +0000137}
138
reed@google.com54c69142013-04-09 15:54:52 +0000139///////////////////////////////////////////////////////////////////////////////
140
reed@google.come49d67e2013-04-22 18:00:06 +0000141// Returns the string from the pattern, or NULL
142static const char* get_name(FcPattern* pattern, const char field[],
143 int index = 0) {
144 const char* name;
145 if (FcPatternGetString(pattern, field, index,
146 (FcChar8**)&name) != FcResultMatch) {
147 name = NULL;
148 }
149 return name;
150}
151
152///////////////////////////////////////////////////////////////////////////////
153
reed@google.comb1c65b62013-02-26 15:50:51 +0000154namespace {
155
156// Equivalence classes, used to match the Liberation and other fonts
157// with their metric-compatible replacements. See the discussion in
158// GetFontEquivClass().
159enum FontEquivClass
160{
161 OTHER,
162 SANS,
163 SERIF,
164 MONO,
165 SYMBOL,
166 PGOTHIC,
167 GOTHIC,
168 PMINCHO,
169 MINCHO,
170 SIMSUN,
171 NSIMSUN,
172 SIMHEI,
173 PMINGLIU,
174 MINGLIU,
175 PMINGLIUHK,
176 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000177 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000178 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000179};
180
181// Match the font name against a whilelist of fonts, returning the equivalence
182// class.
183FontEquivClass GetFontEquivClass(const char* fontname)
184{
185 // It would be nice for fontconfig to tell us whether a given suggested
186 // replacement is a "strong" match (that is, an equivalent font) or
187 // a "weak" match (that is, fontconfig's next-best attempt at finding a
188 // substitute). However, I played around with the fontconfig API for
189 // a good few hours and could not make it reveal this information.
190 //
191 // So instead, we hardcode. Initially this function emulated
192 // /etc/fonts/conf.d/30-metric-aliases.conf
193 // from my Ubuntu system, but we're better off being very conservative.
194
195 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
196 // Arial, Times New Roman and Courier New with a character repertoire
197 // much larger than Liberation. Note that Cousine is metrically
198 // compatible with Courier New, but the former is sans-serif while
199 // the latter is serif.
200
201
202 struct FontEquivMap {
203 FontEquivClass clazz;
204 const char name[40];
205 };
206
207 static const FontEquivMap kFontEquivMap[] = {
208 { SANS, "Arial" },
209 { SANS, "Arimo" },
210 { SANS, "Liberation Sans" },
211
212 { SERIF, "Times New Roman" },
213 { SERIF, "Tinos" },
214 { SERIF, "Liberation Serif" },
215
216 { MONO, "Courier New" },
217 { MONO, "Cousine" },
218 { MONO, "Liberation Mono" },
219
220 { SYMBOL, "Symbol" },
221 { SYMBOL, "Symbol Neu" },
222
223 // MS Pゴシック
224 { PGOTHIC, "MS PGothic" },
225 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
226 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
227 { PGOTHIC, "IPAPGothic" },
228 { PGOTHIC, "MotoyaG04Gothic" },
229
230 // MS ゴシック
231 { GOTHIC, "MS Gothic" },
232 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
233 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
234 { GOTHIC, "IPAGothic" },
235 { GOTHIC, "MotoyaG04GothicMono" },
236
237 // MS P明朝
238 { PMINCHO, "MS PMincho" },
239 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
240 "\xe6\x98\x8e\xe6\x9c\x9d"},
241 { PMINCHO, "IPAPMincho" },
242 { PMINCHO, "MotoyaG04Mincho" },
243
244 // MS 明朝
245 { MINCHO, "MS Mincho" },
246 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
247 { MINCHO, "IPAMincho" },
248 { MINCHO, "MotoyaG04MinchoMono" },
249
250 // 宋体
251 { SIMSUN, "Simsun" },
252 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
253 { SIMSUN, "MSung GB18030" },
254 { SIMSUN, "Song ASC" },
255
256 // 新宋体
257 { NSIMSUN, "NSimsun" },
258 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
259 { NSIMSUN, "MSung GB18030" },
260 { NSIMSUN, "N Song ASC" },
261
262 // 黑体
263 { SIMHEI, "Simhei" },
264 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
265 { SIMHEI, "MYingHeiGB18030" },
266 { SIMHEI, "MYingHeiB5HK" },
267
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000268 // 新細明體
269 { PMINGLIU, "PMingLiU"},
270 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
271 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000272
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000273 // 細明體
274 { MINGLIU, "MingLiU"},
275 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
276 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000277
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000278 // 新細明體
279 { PMINGLIUHK, "PMingLiU_HKSCS"},
280 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
281 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000282
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000283 // 細明體
284 { MINGLIUHK, "MingLiU_HKSCS"},
285 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
286 { MINGLIUHK, "MSung B5HK"},
287
288 // Cambria
289 { CAMBRIA, "Cambria" },
290 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000291
292 // Calibri
293 { CALIBRI, "Calibri" },
294 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000295 };
296
297 static const size_t kFontCount =
298 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
299
300 // TODO(jungshik): If this loop turns out to be hot, turn
301 // the array to a static (hash)map to speed it up.
302 for (size_t i = 0; i < kFontCount; ++i) {
303 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
304 return kFontEquivMap[i].clazz;
305 }
306 return OTHER;
307}
308
309
310// Return true if |font_a| and |font_b| are visually and at the metrics
311// level interchangeable.
312bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
313{
314 FontEquivClass class_a = GetFontEquivClass(font_a);
315 FontEquivClass class_b = GetFontEquivClass(font_b);
316
317 return class_a != OTHER && class_a == class_b;
318}
319
reed@google.comb1c65b62013-02-26 15:50:51 +0000320// Normally we only return exactly the font asked for. In last-resort
321// cases, the request either doesn't specify a font or is one of the
322// basic font names like "Sans", "Serif" or "Monospace". This function
323// tells you whether a given request is for such a fallback.
324bool IsFallbackFontAllowed(const std::string& family) {
325 const char* family_cstr = family.c_str();
326 return family.empty() ||
327 strcasecmp(family_cstr, "sans") == 0 ||
328 strcasecmp(family_cstr, "serif") == 0 ||
329 strcasecmp(family_cstr, "monospace") == 0;
330}
331
reed@google.come49d67e2013-04-22 18:00:06 +0000332static bool valid_pattern(FcPattern* pattern) {
333 FcBool is_scalable;
334 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
335 || !is_scalable) {
336 return false;
337 }
338
339 // fontconfig can also return fonts which are unreadable
340 const char* c_filename = get_name(pattern, FC_FILE);
341 if (!c_filename) {
342 return false;
343 }
344 if (access(c_filename, R_OK) != 0) {
345 return false;
346 }
347 return true;
348}
349
reed@google.comb1c65b62013-02-26 15:50:51 +0000350// Find matching font from |font_set| for the given font family.
351FcPattern* MatchFont(FcFontSet* font_set,
reed@google.come49d67e2013-04-22 18:00:06 +0000352 const char* post_config_family,
reed@google.comb1c65b62013-02-26 15:50:51 +0000353 const std::string& family) {
354 // Older versions of fontconfig have a bug where they cannot select
355 // only scalable fonts so we have to manually filter the results.
356 FcPattern* match = NULL;
357 for (int i = 0; i < font_set->nfont; ++i) {
358 FcPattern* current = font_set->fonts[i];
reed@google.come49d67e2013-04-22 18:00:06 +0000359 if (valid_pattern(current)) {
360 match = current;
361 break;
reed@google.comb1c65b62013-02-26 15:50:51 +0000362 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000363 }
364
365 if (match && !IsFallbackFontAllowed(family)) {
366 bool acceptable_substitute = false;
367 for (int id = 0; id < 255; ++id) {
reed@google.come49d67e2013-04-22 18:00:06 +0000368 const char* post_match_family = get_name(match, FC_FAMILY, id);
369 if (!post_match_family)
reed@google.comb1c65b62013-02-26 15:50:51 +0000370 break;
371 acceptable_substitute =
reed@google.come49d67e2013-04-22 18:00:06 +0000372 (strcasecmp(post_config_family, post_match_family) == 0 ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000373 // Workaround for Issue 12530:
374 // requested family: "Bitstream Vera Sans"
375 // post_config_family: "Arial"
376 // post_match_family: "Bitstream Vera Sans"
377 // -> We should treat this case as a good match.
reed@google.come49d67e2013-04-22 18:00:06 +0000378 strcasecmp(family.c_str(), post_match_family) == 0) ||
379 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000380 if (acceptable_substitute)
381 break;
382 }
383 if (!acceptable_substitute)
384 return NULL;
385 }
386
387 return match;
388}
389
390// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000391SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000392 int resulting_bold;
393 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
394 resulting_bold = FC_WEIGHT_NORMAL;
395
396 int resulting_italic;
397 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
398 resulting_italic = FC_SLANT_ROMAN;
399
400 // If we ask for an italic font, fontconfig might take a roman font and set
401 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
402 // that the font is italic or oblique. So, if we see a matrix, we don't
403 // believe that it's italic.
404 FcValue matrix;
405 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
406
407 // If we ask for an italic font, fontconfig might take a roman font and set
408 // FC_EMBOLDEN.
409 FcValue embolden;
410 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
411
412 int styleBits = 0;
413 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
414 styleBits |= SkTypeface::kBold;
415 }
416 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
417 styleBits |= SkTypeface::kItalic;
418 }
419
reed@google.comf71a2332013-02-27 19:06:30 +0000420 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000421}
422
423} // anonymous namespace
424
425///////////////////////////////////////////////////////////////////////////////
426
reed@google.comf71a2332013-02-27 19:06:30 +0000427#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000428
reed@google.comf71a2332013-02-27 19:06:30 +0000429SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000430 SkAutoMutexAcquire ac(mutex_);
431
reed@google.comb1c65b62013-02-26 15:50:51 +0000432 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000433
434 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000435}
436
437SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
438}
439
reed@google.comf71a2332013-02-27 19:06:30 +0000440bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
441 SkTypeface::Style style,
442 FontIdentity* outIdentity,
443 SkString* outFamilyName,
444 SkTypeface::Style* outStyle) {
reed@google.comee619a02013-02-26 22:58:09 +0000445 std::string familyStr(familyName ? familyName : "");
446 if (familyStr.length() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000447 return false;
448 }
449
450 SkAutoMutexAcquire ac(mutex_);
451
reed@google.comb1c65b62013-02-26 15:50:51 +0000452 FcPattern* pattern = FcPatternCreate();
453
reed@google.comee619a02013-02-26 22:58:09 +0000454 if (familyName) {
455 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
456 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000457 FcPatternAddInteger(pattern, FC_WEIGHT,
458 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
459 : FC_WEIGHT_NORMAL);
460 FcPatternAddInteger(pattern, FC_SLANT,
461 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
462 : FC_SLANT_ROMAN);
463 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
464
465 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
466 FcDefaultSubstitute(pattern);
467
468 // Font matching:
469 // CSS often specifies a fallback list of families:
470 // font-family: a, b, c, serif;
471 // However, fontconfig will always do its best to find *a* font when asked
472 // for something so we need a way to tell if the match which it has found is
473 // "good enough" for us. Otherwise, we can return NULL which gets piped up
474 // and lets WebKit know to try the next CSS family name. However, fontconfig
475 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
476 // wish to support that.
477 //
478 // Thus, if a specific family is requested we set @family_requested. Then we
479 // record two strings: the family name after config processing and the
480 // family name after resolving. If the two are equal, it's a good match.
481 //
482 // So consider the case where a user has mapped Arial to Helvetica in their
483 // config.
484 // requested family: "Arial"
485 // post_config_family: "Helvetica"
486 // post_match_family: "Helvetica"
487 // -> good match
488 //
489 // and for a missing font:
490 // requested family: "Monaco"
491 // post_config_family: "Monaco"
492 // post_match_family: "Times New Roman"
493 // -> BAD match
494 //
495 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000496
497 const char* post_config_family = get_name(pattern, FC_FAMILY);
498 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000499 // we can just continue with an empty name, e.g. default font
500 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000501 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000502
503 FcResult result;
504 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
505 if (!font_set) {
506 FcPatternDestroy(pattern);
507 return false;
508 }
509
reed@google.comee619a02013-02-26 22:58:09 +0000510 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000511 if (!match) {
512 FcPatternDestroy(pattern);
513 FcFontSetDestroy(font_set);
514 return false;
515 }
516
517 FcPatternDestroy(pattern);
518
reed@google.comf71a2332013-02-27 19:06:30 +0000519 // From here out we just extract our results from 'match'
520
reed@google.come49d67e2013-04-22 18:00:06 +0000521 post_config_family = get_name(match, FC_FAMILY);
522 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000523 FcFontSetDestroy(font_set);
524 return false;
525 }
526
reed@google.come49d67e2013-04-22 18:00:06 +0000527 const char* c_filename = get_name(match, FC_FILE);
528 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000529 FcFontSetDestroy(font_set);
530 return false;
531 }
reed@google.comf71a2332013-02-27 19:06:30 +0000532
reed@google.comb1c65b62013-02-26 15:50:51 +0000533 int face_index;
534 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
535 FcFontSetDestroy(font_set);
536 return false;
537 }
538
reed@google.comb1c65b62013-02-26 15:50:51 +0000539 FcFontSetDestroy(font_set);
540
reed@google.comf71a2332013-02-27 19:06:30 +0000541 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000542 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000543 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000544 }
reed@google.comf71a2332013-02-27 19:06:30 +0000545 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000546 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000547 }
reed@google.comf71a2332013-02-27 19:06:30 +0000548 if (outStyle) {
549 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000550 }
reed@google.comee619a02013-02-26 22:58:09 +0000551 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000552}
553
reed@google.comf71a2332013-02-27 19:06:30 +0000554SkStream* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000555 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000556}
reed@google.com027fd202013-04-19 20:45:30 +0000557
558///////////////////////////////////////////////////////////////////////////////
559
reed@google.com027fd202013-04-19 20:45:30 +0000560static bool find_name(const SkTDArray<const char*>& list, const char* str) {
561 int count = list.count();
562 for (int i = 0; i < count; ++i) {
563 if (!strcmp(list[i], str)) {
564 return true;
565 }
566 }
567 return false;
568}
569
570SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000571 SkAutoMutexAcquire ac(mutex_);
572
reed@google.com027fd202013-04-19 20:45:30 +0000573 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000574 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
575 if (NULL == pat) {
576 return NULL;
577 }
578
579 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
580 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
reed@google.com027fd202013-04-19 20:45:30 +0000581 if (NULL == os) {
reed@google.comdf798b32013-04-19 20:52:50 +0000582 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000583 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000584
reed@google.com027fd202013-04-19 20:45:30 +0000585 FcFontSet* fs = FcFontList(NULL, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000586 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
reed@google.com027fd202013-04-19 20:45:30 +0000587 if (NULL == fs) {
reed@google.comdf798b32013-04-19 20:52:50 +0000588 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000589 }
590
591 SkTDArray<const char*> names;
592 SkTDArray<size_t> sizes;
593 for (int i = 0; i < fs->nfont; ++i) {
594 FcPattern* match = fs->fonts[i];
595 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000596 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000597 *names.append() = famName;
598 *sizes.append() = strlen(famName) + 1;
599 }
600 }
601
reed@google.com027fd202013-04-19 20:45:30 +0000602 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
603 sizes.begin(), names.count());
604}
605
606bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
607 SkString* outFamilyName,
608 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000609 SkAutoMutexAcquire ac(mutex_);
610
611#if 0
612 std::string familyStr(familyName ? familyName : "");
613 if (familyStr.length() > kMaxFontFamilyLength) {
614 return false;
615 }
616
617 SkAutoMutexAcquire ac(mutex_);
618
619 FcPattern* pattern = FcPatternCreate();
620
621 if (familyName) {
622 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
623 }
624 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
625
626 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
627 FcDefaultSubstitute(pattern);
628
629 // Font matching:
630 // CSS often specifies a fallback list of families:
631 // font-family: a, b, c, serif;
632 // However, fontconfig will always do its best to find *a* font when asked
633 // for something so we need a way to tell if the match which it has found is
634 // "good enough" for us. Otherwise, we can return NULL which gets piped up
635 // and lets WebKit know to try the next CSS family name. However, fontconfig
636 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
637 // wish to support that.
638 //
639 // Thus, if a specific family is requested we set @family_requested. Then we
640 // record two strings: the family name after config processing and the
641 // family name after resolving. If the two are equal, it's a good match.
642 //
643 // So consider the case where a user has mapped Arial to Helvetica in their
644 // config.
645 // requested family: "Arial"
646 // post_config_family: "Helvetica"
647 // post_match_family: "Helvetica"
648 // -> good match
649 //
650 // and for a missing font:
651 // requested family: "Monaco"
652 // post_config_family: "Monaco"
653 // post_match_family: "Times New Roman"
654 // -> BAD match
655 //
656 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
657
658 const char* post_config_family = get_name(pattern, FC_FAMILY);
659
660 FcResult result;
661 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
662 if (!font_set) {
663 FcPatternDestroy(pattern);
664 return false;
665 }
666
667 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
668 if (!match) {
669 FcPatternDestroy(pattern);
670 FcFontSetDestroy(font_set);
671 return false;
672 }
673
674 FcPatternDestroy(pattern);
675
676 // From here out we just extract our results from 'match'
677
678 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
679 FcFontSetDestroy(font_set);
680 return false;
681 }
682
683 FcChar8* c_filename;
684 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
685 FcFontSetDestroy(font_set);
686 return false;
687 }
688
689 int face_index;
690 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
691 FcFontSetDestroy(font_set);
692 return false;
693 }
694
695 FcFontSetDestroy(font_set);
696
697 if (outIdentity) {
698 outIdentity->fTTCIndex = face_index;
699 outIdentity->fString.set((const char*)c_filename);
700 }
701 if (outFamilyName) {
702 outFamilyName->set((const char*)post_config_family);
703 }
704 if (outStyle) {
705 *outStyle = GetFontStyle(match);
706 }
707 return true;
708
709////////////////////
710
711 int count;
712 FcPattern** match = MatchFont(font_set, post_config_family, &count);
713 if (!match) {
714 FcPatternDestroy(pattern);
715 FcFontSetDestroy(font_set);
716 return NULL;
717 }
718
719 FcPatternDestroy(pattern);
720
721 SkTDArray<FcPattern*> trimmedMatches;
722 for (int i = 0; i < count; ++i) {
723 const char* justName = find_just_name(get_name(match[i], FC_FILE));
724 if (!is_lower(*justName)) {
725 *trimmedMatches.append() = match[i];
726 }
727 }
728
729 SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
730 (trimmedMatches.begin(),
731 trimmedMatches.count()));
732#endif
reed@google.com027fd202013-04-19 20:45:30 +0000733 return false;
734}