blob: 2c1e4188b9d2623659157200ad195299084a4cc5 [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
45 fID = buffer.readU32();
46 fTTCIndex = buffer.readU32();
47 size_t strLen = buffer.readU32();
48 int weight = buffer.readU32();
49 int width = buffer.readU32();
50 SkFontStyle::Slant slant = (SkFontStyle::Slant)buffer.readU8();
51 fStyle = SkFontStyle(weight, width, slant);
52 fString.resize(strLen);
53 buffer.read(fString.writable_str(), strLen);
54 buffer.skipToAlign4();
55
56 return buffer.pos(); // the actual number of bytes read
57}
58
59#ifdef SK_DEBUG
60static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
61 iden->fID = 10;
62 iden->fTTCIndex = 2;
63 iden->fString.set("Hello world");
64 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
65}
66
67static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
68 int initValue) {
69 SkFontConfigInterface::FontIdentity iden1;
70
71 size_t size0 = iden0.writeToMemory(NULL);
72
73 SkAutoMalloc storage(size0);
74 memset(storage.get(), initValue, size0);
75
76 size_t size1 = iden0.writeToMemory(storage.get());
77 SkASSERT(size0 == size1);
78
79 SkASSERT(iden0 != iden1);
80 size_t size2 = iden1.readFromMemory(storage.get(), size1);
81 SkASSERT(size2 == size1);
82 SkASSERT(iden0 == iden1);
83}
84
85static void fontconfiginterface_unittest() {
86 SkFontConfigInterface::FontIdentity iden0, iden1;
87
88 SkASSERT(iden0 == iden1);
89
90 make_iden(&iden0);
91 SkASSERT(iden0 != iden1);
92
93 make_iden(&iden1);
94 SkASSERT(iden0 == iden1);
95
96 test_writeToMemory(iden0, 0);
97 test_writeToMemory(iden0, 0);
98}
99#endif
100
reed@google.comb1c65b62013-02-26 15:50:51 +0000101class SkFontConfigInterfaceDirect : public SkFontConfigInterface {
102public:
103 SkFontConfigInterfaceDirect();
104 virtual ~SkFontConfigInterfaceDirect();
105
reed@google.comf71a2332013-02-27 19:06:30 +0000106 virtual bool matchFamilyName(const char familyName[],
107 SkTypeface::Style requested,
108 FontIdentity* outFontIdentifier,
109 SkString* outFamilyName,
110 SkTypeface::Style* outStyle) SK_OVERRIDE;
111 virtual SkStream* openStream(const FontIdentity&) SK_OVERRIDE;
reed@google.comb1c65b62013-02-26 15:50:51 +0000112
reed@google.com027fd202013-04-19 20:45:30 +0000113 // new APIs
114 virtual SkDataTable* getFamilyNames() SK_OVERRIDE;
115 virtual bool matchFamilySet(const char inFamilyName[],
116 SkString* outFamilyName,
117 SkTArray<FontIdentity>*) SK_OVERRIDE;
118
reed@google.comb1c65b62013-02-26 15:50:51 +0000119private:
120 SkMutex mutex_;
reed@google.comb1c65b62013-02-26 15:50:51 +0000121};
122
reed@google.comd66045e2013-03-04 19:07:02 +0000123SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface() {
124 static SkFontConfigInterface* gDirect;
125 if (NULL == gDirect) {
reed@google.com750a24b2013-04-22 18:45:12 +0000126 static SkMutex gMutex;
127 SkAutoMutexAcquire ac(gMutex);
128
129 if (NULL == gDirect) {
130 gDirect = new SkFontConfigInterfaceDirect;
131 }
reed@google.comd66045e2013-03-04 19:07:02 +0000132 }
133 return gDirect;
reed@google.comb1c65b62013-02-26 15:50:51 +0000134}
135
reed@google.com54c69142013-04-09 15:54:52 +0000136///////////////////////////////////////////////////////////////////////////////
137
reed@google.come49d67e2013-04-22 18:00:06 +0000138// Returns the string from the pattern, or NULL
139static const char* get_name(FcPattern* pattern, const char field[],
140 int index = 0) {
141 const char* name;
142 if (FcPatternGetString(pattern, field, index,
143 (FcChar8**)&name) != FcResultMatch) {
144 name = NULL;
145 }
146 return name;
147}
148
149///////////////////////////////////////////////////////////////////////////////
150
reed@google.comb1c65b62013-02-26 15:50:51 +0000151namespace {
152
153// Equivalence classes, used to match the Liberation and other fonts
154// with their metric-compatible replacements. See the discussion in
155// GetFontEquivClass().
156enum FontEquivClass
157{
158 OTHER,
159 SANS,
160 SERIF,
161 MONO,
162 SYMBOL,
163 PGOTHIC,
164 GOTHIC,
165 PMINCHO,
166 MINCHO,
167 SIMSUN,
168 NSIMSUN,
169 SIMHEI,
170 PMINGLIU,
171 MINGLIU,
172 PMINGLIUHK,
173 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000174 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000175 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000176};
177
178// Match the font name against a whilelist of fonts, returning the equivalence
179// class.
180FontEquivClass GetFontEquivClass(const char* fontname)
181{
182 // It would be nice for fontconfig to tell us whether a given suggested
183 // replacement is a "strong" match (that is, an equivalent font) or
184 // a "weak" match (that is, fontconfig's next-best attempt at finding a
185 // substitute). However, I played around with the fontconfig API for
186 // a good few hours and could not make it reveal this information.
187 //
188 // So instead, we hardcode. Initially this function emulated
189 // /etc/fonts/conf.d/30-metric-aliases.conf
190 // from my Ubuntu system, but we're better off being very conservative.
191
192 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
193 // Arial, Times New Roman and Courier New with a character repertoire
194 // much larger than Liberation. Note that Cousine is metrically
195 // compatible with Courier New, but the former is sans-serif while
196 // the latter is serif.
197
198
199 struct FontEquivMap {
200 FontEquivClass clazz;
201 const char name[40];
202 };
203
204 static const FontEquivMap kFontEquivMap[] = {
205 { SANS, "Arial" },
206 { SANS, "Arimo" },
207 { SANS, "Liberation Sans" },
208
209 { SERIF, "Times New Roman" },
210 { SERIF, "Tinos" },
211 { SERIF, "Liberation Serif" },
212
213 { MONO, "Courier New" },
214 { MONO, "Cousine" },
215 { MONO, "Liberation Mono" },
216
217 { SYMBOL, "Symbol" },
218 { SYMBOL, "Symbol Neu" },
219
220 // MS Pゴシック
221 { PGOTHIC, "MS PGothic" },
222 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
223 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
224 { PGOTHIC, "IPAPGothic" },
225 { PGOTHIC, "MotoyaG04Gothic" },
226
227 // MS ゴシック
228 { GOTHIC, "MS Gothic" },
229 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
230 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
231 { GOTHIC, "IPAGothic" },
232 { GOTHIC, "MotoyaG04GothicMono" },
233
234 // MS P明朝
235 { PMINCHO, "MS PMincho" },
236 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
237 "\xe6\x98\x8e\xe6\x9c\x9d"},
238 { PMINCHO, "IPAPMincho" },
239 { PMINCHO, "MotoyaG04Mincho" },
240
241 // MS 明朝
242 { MINCHO, "MS Mincho" },
243 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
244 { MINCHO, "IPAMincho" },
245 { MINCHO, "MotoyaG04MinchoMono" },
246
247 // 宋体
248 { SIMSUN, "Simsun" },
249 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
250 { SIMSUN, "MSung GB18030" },
251 { SIMSUN, "Song ASC" },
252
253 // 新宋体
254 { NSIMSUN, "NSimsun" },
255 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
256 { NSIMSUN, "MSung GB18030" },
257 { NSIMSUN, "N Song ASC" },
258
259 // 黑体
260 { SIMHEI, "Simhei" },
261 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
262 { SIMHEI, "MYingHeiGB18030" },
263 { SIMHEI, "MYingHeiB5HK" },
264
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000265 // 新細明體
266 { PMINGLIU, "PMingLiU"},
267 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
268 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000269
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000270 // 細明體
271 { MINGLIU, "MingLiU"},
272 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
273 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000274
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000275 // 新細明體
276 { PMINGLIUHK, "PMingLiU_HKSCS"},
277 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
278 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000279
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000280 // 細明體
281 { MINGLIUHK, "MingLiU_HKSCS"},
282 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
283 { MINGLIUHK, "MSung B5HK"},
284
285 // Cambria
286 { CAMBRIA, "Cambria" },
287 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000288
289 // Calibri
290 { CALIBRI, "Calibri" },
291 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000292 };
293
294 static const size_t kFontCount =
295 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
296
297 // TODO(jungshik): If this loop turns out to be hot, turn
298 // the array to a static (hash)map to speed it up.
299 for (size_t i = 0; i < kFontCount; ++i) {
300 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
301 return kFontEquivMap[i].clazz;
302 }
303 return OTHER;
304}
305
306
307// Return true if |font_a| and |font_b| are visually and at the metrics
308// level interchangeable.
309bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
310{
311 FontEquivClass class_a = GetFontEquivClass(font_a);
312 FontEquivClass class_b = GetFontEquivClass(font_b);
313
314 return class_a != OTHER && class_a == class_b;
315}
316
reed@google.comb1c65b62013-02-26 15:50:51 +0000317// Normally we only return exactly the font asked for. In last-resort
318// cases, the request either doesn't specify a font or is one of the
319// basic font names like "Sans", "Serif" or "Monospace". This function
320// tells you whether a given request is for such a fallback.
321bool IsFallbackFontAllowed(const std::string& family) {
322 const char* family_cstr = family.c_str();
323 return family.empty() ||
324 strcasecmp(family_cstr, "sans") == 0 ||
325 strcasecmp(family_cstr, "serif") == 0 ||
326 strcasecmp(family_cstr, "monospace") == 0;
327}
328
reed@google.come49d67e2013-04-22 18:00:06 +0000329static bool valid_pattern(FcPattern* pattern) {
330 FcBool is_scalable;
331 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
332 || !is_scalable) {
333 return false;
334 }
335
336 // fontconfig can also return fonts which are unreadable
337 const char* c_filename = get_name(pattern, FC_FILE);
338 if (!c_filename) {
339 return false;
340 }
341 if (access(c_filename, R_OK) != 0) {
342 return false;
343 }
344 return true;
345}
346
reed@google.comb1c65b62013-02-26 15:50:51 +0000347// Find matching font from |font_set| for the given font family.
348FcPattern* MatchFont(FcFontSet* font_set,
reed@google.come49d67e2013-04-22 18:00:06 +0000349 const char* post_config_family,
reed@google.comb1c65b62013-02-26 15:50:51 +0000350 const std::string& family) {
351 // Older versions of fontconfig have a bug where they cannot select
352 // only scalable fonts so we have to manually filter the results.
353 FcPattern* match = NULL;
354 for (int i = 0; i < font_set->nfont; ++i) {
355 FcPattern* current = font_set->fonts[i];
reed@google.come49d67e2013-04-22 18:00:06 +0000356 if (valid_pattern(current)) {
357 match = current;
358 break;
reed@google.comb1c65b62013-02-26 15:50:51 +0000359 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000360 }
361
362 if (match && !IsFallbackFontAllowed(family)) {
363 bool acceptable_substitute = false;
364 for (int id = 0; id < 255; ++id) {
reed@google.come49d67e2013-04-22 18:00:06 +0000365 const char* post_match_family = get_name(match, FC_FAMILY, id);
366 if (!post_match_family)
reed@google.comb1c65b62013-02-26 15:50:51 +0000367 break;
368 acceptable_substitute =
reed@google.come49d67e2013-04-22 18:00:06 +0000369 (strcasecmp(post_config_family, post_match_family) == 0 ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000370 // Workaround for Issue 12530:
371 // requested family: "Bitstream Vera Sans"
372 // post_config_family: "Arial"
373 // post_match_family: "Bitstream Vera Sans"
374 // -> We should treat this case as a good match.
reed@google.come49d67e2013-04-22 18:00:06 +0000375 strcasecmp(family.c_str(), post_match_family) == 0) ||
376 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000377 if (acceptable_substitute)
378 break;
379 }
380 if (!acceptable_substitute)
381 return NULL;
382 }
383
384 return match;
385}
386
387// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000388SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000389 int resulting_bold;
390 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
391 resulting_bold = FC_WEIGHT_NORMAL;
392
393 int resulting_italic;
394 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
395 resulting_italic = FC_SLANT_ROMAN;
396
397 // If we ask for an italic font, fontconfig might take a roman font and set
398 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
399 // that the font is italic or oblique. So, if we see a matrix, we don't
400 // believe that it's italic.
401 FcValue matrix;
402 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
403
404 // If we ask for an italic font, fontconfig might take a roman font and set
405 // FC_EMBOLDEN.
406 FcValue embolden;
407 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
408
409 int styleBits = 0;
410 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
411 styleBits |= SkTypeface::kBold;
412 }
413 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
414 styleBits |= SkTypeface::kItalic;
415 }
416
reed@google.comf71a2332013-02-27 19:06:30 +0000417 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000418}
419
420} // anonymous namespace
421
422///////////////////////////////////////////////////////////////////////////////
423
reed@google.comf71a2332013-02-27 19:06:30 +0000424#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000425
reed@google.comf71a2332013-02-27 19:06:30 +0000426SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000427 SkAutoMutexAcquire ac(mutex_);
428
reed@google.comb1c65b62013-02-26 15:50:51 +0000429 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000430
431 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000432}
433
434SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
435}
436
reed@google.comf71a2332013-02-27 19:06:30 +0000437bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
438 SkTypeface::Style style,
439 FontIdentity* outIdentity,
440 SkString* outFamilyName,
441 SkTypeface::Style* outStyle) {
reed@google.comee619a02013-02-26 22:58:09 +0000442 std::string familyStr(familyName ? familyName : "");
443 if (familyStr.length() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000444 return false;
445 }
446
447 SkAutoMutexAcquire ac(mutex_);
448
reed@google.comb1c65b62013-02-26 15:50:51 +0000449 FcPattern* pattern = FcPatternCreate();
450
reed@google.comee619a02013-02-26 22:58:09 +0000451 if (familyName) {
452 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
453 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000454 FcPatternAddInteger(pattern, FC_WEIGHT,
455 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
456 : FC_WEIGHT_NORMAL);
457 FcPatternAddInteger(pattern, FC_SLANT,
458 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
459 : FC_SLANT_ROMAN);
460 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
461
462 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
463 FcDefaultSubstitute(pattern);
464
465 // Font matching:
466 // CSS often specifies a fallback list of families:
467 // font-family: a, b, c, serif;
468 // However, fontconfig will always do its best to find *a* font when asked
469 // for something so we need a way to tell if the match which it has found is
470 // "good enough" for us. Otherwise, we can return NULL which gets piped up
471 // and lets WebKit know to try the next CSS family name. However, fontconfig
472 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
473 // wish to support that.
474 //
475 // Thus, if a specific family is requested we set @family_requested. Then we
476 // record two strings: the family name after config processing and the
477 // family name after resolving. If the two are equal, it's a good match.
478 //
479 // So consider the case where a user has mapped Arial to Helvetica in their
480 // config.
481 // requested family: "Arial"
482 // post_config_family: "Helvetica"
483 // post_match_family: "Helvetica"
484 // -> good match
485 //
486 // and for a missing font:
487 // requested family: "Monaco"
488 // post_config_family: "Monaco"
489 // post_match_family: "Times New Roman"
490 // -> BAD match
491 //
492 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000493
494 const char* post_config_family = get_name(pattern, FC_FAMILY);
495 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000496 // we can just continue with an empty name, e.g. default font
497 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000498 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000499
500 FcResult result;
501 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
502 if (!font_set) {
503 FcPatternDestroy(pattern);
504 return false;
505 }
506
reed@google.comee619a02013-02-26 22:58:09 +0000507 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000508 if (!match) {
509 FcPatternDestroy(pattern);
510 FcFontSetDestroy(font_set);
511 return false;
512 }
513
514 FcPatternDestroy(pattern);
515
reed@google.comf71a2332013-02-27 19:06:30 +0000516 // From here out we just extract our results from 'match'
517
reed@google.come49d67e2013-04-22 18:00:06 +0000518 post_config_family = get_name(match, FC_FAMILY);
519 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000520 FcFontSetDestroy(font_set);
521 return false;
522 }
523
reed@google.come49d67e2013-04-22 18:00:06 +0000524 const char* c_filename = get_name(match, FC_FILE);
525 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000526 FcFontSetDestroy(font_set);
527 return false;
528 }
reed@google.comf71a2332013-02-27 19:06:30 +0000529
reed@google.comb1c65b62013-02-26 15:50:51 +0000530 int face_index;
531 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
532 FcFontSetDestroy(font_set);
533 return false;
534 }
535
reed@google.comb1c65b62013-02-26 15:50:51 +0000536 FcFontSetDestroy(font_set);
537
reed@google.comf71a2332013-02-27 19:06:30 +0000538 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000539 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000540 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000541 }
reed@google.comf71a2332013-02-27 19:06:30 +0000542 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000543 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000544 }
reed@google.comf71a2332013-02-27 19:06:30 +0000545 if (outStyle) {
546 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000547 }
reed@google.comee619a02013-02-26 22:58:09 +0000548 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000549}
550
reed@google.comf71a2332013-02-27 19:06:30 +0000551SkStream* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000552 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000553}
reed@google.com027fd202013-04-19 20:45:30 +0000554
555///////////////////////////////////////////////////////////////////////////////
556
reed@google.com027fd202013-04-19 20:45:30 +0000557static bool find_name(const SkTDArray<const char*>& list, const char* str) {
558 int count = list.count();
559 for (int i = 0; i < count; ++i) {
560 if (!strcmp(list[i], str)) {
561 return true;
562 }
563 }
564 return false;
565}
566
567SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000568 SkAutoMutexAcquire ac(mutex_);
569
reed@google.com027fd202013-04-19 20:45:30 +0000570 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000571 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
572 if (NULL == pat) {
573 return NULL;
574 }
575
576 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
577 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
reed@google.com027fd202013-04-19 20:45:30 +0000578 if (NULL == os) {
reed@google.comdf798b32013-04-19 20:52:50 +0000579 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000580 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000581
reed@google.com027fd202013-04-19 20:45:30 +0000582 FcFontSet* fs = FcFontList(NULL, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000583 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
reed@google.com027fd202013-04-19 20:45:30 +0000584 if (NULL == fs) {
reed@google.comdf798b32013-04-19 20:52:50 +0000585 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000586 }
587
588 SkTDArray<const char*> names;
589 SkTDArray<size_t> sizes;
590 for (int i = 0; i < fs->nfont; ++i) {
591 FcPattern* match = fs->fonts[i];
592 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000593 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000594 *names.append() = famName;
595 *sizes.append() = strlen(famName) + 1;
596 }
597 }
598
reed@google.com027fd202013-04-19 20:45:30 +0000599 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
600 sizes.begin(), names.count());
601}
602
603bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
604 SkString* outFamilyName,
605 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000606 SkAutoMutexAcquire ac(mutex_);
607
608#if 0
609 std::string familyStr(familyName ? familyName : "");
610 if (familyStr.length() > kMaxFontFamilyLength) {
611 return false;
612 }
613
614 SkAutoMutexAcquire ac(mutex_);
615
616 FcPattern* pattern = FcPatternCreate();
617
618 if (familyName) {
619 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
620 }
621 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
622
623 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
624 FcDefaultSubstitute(pattern);
625
626 // Font matching:
627 // CSS often specifies a fallback list of families:
628 // font-family: a, b, c, serif;
629 // However, fontconfig will always do its best to find *a* font when asked
630 // for something so we need a way to tell if the match which it has found is
631 // "good enough" for us. Otherwise, we can return NULL which gets piped up
632 // and lets WebKit know to try the next CSS family name. However, fontconfig
633 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
634 // wish to support that.
635 //
636 // Thus, if a specific family is requested we set @family_requested. Then we
637 // record two strings: the family name after config processing and the
638 // family name after resolving. If the two are equal, it's a good match.
639 //
640 // So consider the case where a user has mapped Arial to Helvetica in their
641 // config.
642 // requested family: "Arial"
643 // post_config_family: "Helvetica"
644 // post_match_family: "Helvetica"
645 // -> good match
646 //
647 // and for a missing font:
648 // requested family: "Monaco"
649 // post_config_family: "Monaco"
650 // post_match_family: "Times New Roman"
651 // -> BAD match
652 //
653 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
654
655 const char* post_config_family = get_name(pattern, FC_FAMILY);
656
657 FcResult result;
658 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
659 if (!font_set) {
660 FcPatternDestroy(pattern);
661 return false;
662 }
663
664 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
665 if (!match) {
666 FcPatternDestroy(pattern);
667 FcFontSetDestroy(font_set);
668 return false;
669 }
670
671 FcPatternDestroy(pattern);
672
673 // From here out we just extract our results from 'match'
674
675 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
676 FcFontSetDestroy(font_set);
677 return false;
678 }
679
680 FcChar8* c_filename;
681 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
682 FcFontSetDestroy(font_set);
683 return false;
684 }
685
686 int face_index;
687 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
688 FcFontSetDestroy(font_set);
689 return false;
690 }
691
692 FcFontSetDestroy(font_set);
693
694 if (outIdentity) {
695 outIdentity->fTTCIndex = face_index;
696 outIdentity->fString.set((const char*)c_filename);
697 }
698 if (outFamilyName) {
699 outFamilyName->set((const char*)post_config_family);
700 }
701 if (outStyle) {
702 *outStyle = GetFontStyle(match);
703 }
704 return true;
705
706////////////////////
707
708 int count;
709 FcPattern** match = MatchFont(font_set, post_config_family, &count);
710 if (!match) {
711 FcPatternDestroy(pattern);
712 FcFontSetDestroy(font_set);
713 return NULL;
714 }
715
716 FcPatternDestroy(pattern);
717
718 SkTDArray<FcPattern*> trimmedMatches;
719 for (int i = 0; i < count; ++i) {
720 const char* justName = find_just_name(get_name(match[i], FC_FILE));
721 if (!is_lower(*justName)) {
722 *trimmedMatches.append() = match[i];
723 }
724 }
725
726 SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
727 (trimmedMatches.begin(),
728 trimmedMatches.count()));
729#endif
reed@google.com027fd202013-04-19 20:45:30 +0000730 return false;
731}