blob: bc3dede40d24c86748eb22cdef5bb649b39a66d3 [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"
mtklein78358bf2014-06-02 08:44:27 -070018#include "SkLazyPtr.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000019#include "SkStream.h"
20
reed@google.comf55061f2013-04-22 18:48:45 +000021size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
22 size_t size = sizeof(fID) + sizeof(fTTCIndex);
23 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
24 size += sizeof(int32_t) + fString.size(); // store length+data
25 if (addr) {
26 SkWBuffer buffer(addr, size);
27
28 buffer.write32(fID);
29 buffer.write32(fTTCIndex);
30 buffer.write32(fString.size());
31 buffer.write32(fStyle.weight());
32 buffer.write32(fStyle.width());
33 buffer.write8(fStyle.slant());
34 buffer.write(fString.c_str(), fString.size());
35 buffer.padToAlign4();
36
37 SkASSERT(buffer.pos() == size);
38 }
39 return size;
40}
41
42size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
43 size_t size) {
44 SkRBuffer buffer(addr, size);
45
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000046 (void)buffer.readU32(&fID);
47 (void)buffer.readS32(&fTTCIndex);
48 uint32_t strLen, weight, width;
49 (void)buffer.readU32(&strLen);
50 (void)buffer.readU32(&weight);
51 (void)buffer.readU32(&width);
52 uint8_t u8;
53 (void)buffer.readU8(&u8);
54 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
reed@google.comf55061f2013-04-22 18:48:45 +000055 fStyle = SkFontStyle(weight, width, slant);
56 fString.resize(strLen);
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000057 (void)buffer.read(fString.writable_str(), strLen);
reed@google.comf55061f2013-04-22 18:48:45 +000058 buffer.skipToAlign4();
59
60 return buffer.pos(); // the actual number of bytes read
61}
62
63#ifdef SK_DEBUG
64static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
65 iden->fID = 10;
66 iden->fTTCIndex = 2;
67 iden->fString.set("Hello world");
68 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
69}
70
71static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
72 int initValue) {
73 SkFontConfigInterface::FontIdentity iden1;
74
75 size_t size0 = iden0.writeToMemory(NULL);
76
77 SkAutoMalloc storage(size0);
78 memset(storage.get(), initValue, size0);
79
80 size_t size1 = iden0.writeToMemory(storage.get());
81 SkASSERT(size0 == size1);
82
83 SkASSERT(iden0 != iden1);
84 size_t size2 = iden1.readFromMemory(storage.get(), size1);
85 SkASSERT(size2 == size1);
86 SkASSERT(iden0 == iden1);
87}
88
89static void fontconfiginterface_unittest() {
90 SkFontConfigInterface::FontIdentity iden0, iden1;
91
92 SkASSERT(iden0 == iden1);
93
94 make_iden(&iden0);
95 SkASSERT(iden0 != iden1);
96
97 make_iden(&iden1);
98 SkASSERT(iden0 == iden1);
99
100 test_writeToMemory(iden0, 0);
101 test_writeToMemory(iden0, 0);
102}
103#endif
104
reed@google.comb1c65b62013-02-26 15:50:51 +0000105class SkFontConfigInterfaceDirect : public SkFontConfigInterface {
106public:
107 SkFontConfigInterfaceDirect();
108 virtual ~SkFontConfigInterfaceDirect();
109
reed@google.comf71a2332013-02-27 19:06:30 +0000110 virtual bool matchFamilyName(const char familyName[],
111 SkTypeface::Style requested,
112 FontIdentity* outFontIdentifier,
113 SkString* outFamilyName,
114 SkTypeface::Style* outStyle) SK_OVERRIDE;
115 virtual SkStream* openStream(const FontIdentity&) SK_OVERRIDE;
reed@google.comb1c65b62013-02-26 15:50:51 +0000116
reed@google.com027fd202013-04-19 20:45:30 +0000117 // new APIs
118 virtual SkDataTable* getFamilyNames() SK_OVERRIDE;
119 virtual bool matchFamilySet(const char inFamilyName[],
120 SkString* outFamilyName,
121 SkTArray<FontIdentity>*) SK_OVERRIDE;
122
reed@google.comb1c65b62013-02-26 15:50:51 +0000123private:
124 SkMutex mutex_;
reed@google.comb1c65b62013-02-26 15:50:51 +0000125};
126
mtklein78358bf2014-06-02 08:44:27 -0700127namespace {
128SkFontConfigInterface* create_direct() { return SkNEW(SkFontConfigInterfaceDirect); }
129} // namespace
130
reed@google.comd66045e2013-03-04 19:07:02 +0000131SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface() {
mtklein78358bf2014-06-02 08:44:27 -0700132 SK_DECLARE_STATIC_LAZY_PTR(SkFontConfigInterface, direct, create_direct);
133 return direct.get();
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) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000330#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
reed@google.come49d67e2013-04-22 18:00:06 +0000331 FcBool is_scalable;
332 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
333 || !is_scalable) {
334 return false;
335 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000336#endif
reed@google.come49d67e2013-04-22 18:00:06 +0000337
338 // fontconfig can also return fonts which are unreadable
339 const char* c_filename = get_name(pattern, FC_FILE);
340 if (!c_filename) {
341 return false;
342 }
343 if (access(c_filename, R_OK) != 0) {
344 return false;
345 }
346 return true;
347}
348
reed@google.comb1c65b62013-02-26 15:50:51 +0000349// Find matching font from |font_set| for the given font family.
350FcPattern* MatchFont(FcFontSet* font_set,
reed@google.come49d67e2013-04-22 18:00:06 +0000351 const char* post_config_family,
reed@google.comb1c65b62013-02-26 15:50:51 +0000352 const std::string& family) {
353 // Older versions of fontconfig have a bug where they cannot select
354 // only scalable fonts so we have to manually filter the results.
355 FcPattern* match = NULL;
356 for (int i = 0; i < font_set->nfont; ++i) {
357 FcPattern* current = font_set->fonts[i];
reed@google.come49d67e2013-04-22 18:00:06 +0000358 if (valid_pattern(current)) {
359 match = current;
360 break;
reed@google.comb1c65b62013-02-26 15:50:51 +0000361 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000362 }
363
364 if (match && !IsFallbackFontAllowed(family)) {
365 bool acceptable_substitute = false;
366 for (int id = 0; id < 255; ++id) {
reed@google.come49d67e2013-04-22 18:00:06 +0000367 const char* post_match_family = get_name(match, FC_FAMILY, id);
368 if (!post_match_family)
reed@google.comb1c65b62013-02-26 15:50:51 +0000369 break;
370 acceptable_substitute =
reed@google.come49d67e2013-04-22 18:00:06 +0000371 (strcasecmp(post_config_family, post_match_family) == 0 ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000372 // Workaround for Issue 12530:
373 // requested family: "Bitstream Vera Sans"
374 // post_config_family: "Arial"
375 // post_match_family: "Bitstream Vera Sans"
376 // -> We should treat this case as a good match.
reed@google.come49d67e2013-04-22 18:00:06 +0000377 strcasecmp(family.c_str(), post_match_family) == 0) ||
378 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000379 if (acceptable_substitute)
380 break;
381 }
382 if (!acceptable_substitute)
383 return NULL;
384 }
385
386 return match;
387}
388
389// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000390SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000391 int resulting_bold;
392 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
393 resulting_bold = FC_WEIGHT_NORMAL;
394
395 int resulting_italic;
396 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
397 resulting_italic = FC_SLANT_ROMAN;
398
399 // If we ask for an italic font, fontconfig might take a roman font and set
400 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
401 // that the font is italic or oblique. So, if we see a matrix, we don't
402 // believe that it's italic.
403 FcValue matrix;
404 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
405
406 // If we ask for an italic font, fontconfig might take a roman font and set
407 // FC_EMBOLDEN.
408 FcValue embolden;
409 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
410
411 int styleBits = 0;
412 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
413 styleBits |= SkTypeface::kBold;
414 }
415 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
416 styleBits |= SkTypeface::kItalic;
417 }
418
reed@google.comf71a2332013-02-27 19:06:30 +0000419 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000420}
421
422} // anonymous namespace
423
424///////////////////////////////////////////////////////////////////////////////
425
reed@google.comf71a2332013-02-27 19:06:30 +0000426#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000427
reed@google.comf71a2332013-02-27 19:06:30 +0000428SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000429 SkAutoMutexAcquire ac(mutex_);
430
reed@google.comb1c65b62013-02-26 15:50:51 +0000431 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000432
433 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000434}
435
436SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
437}
438
reed@google.comf71a2332013-02-27 19:06:30 +0000439bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
440 SkTypeface::Style style,
441 FontIdentity* outIdentity,
442 SkString* outFamilyName,
443 SkTypeface::Style* outStyle) {
reed@google.comee619a02013-02-26 22:58:09 +0000444 std::string familyStr(familyName ? familyName : "");
445 if (familyStr.length() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000446 return false;
447 }
448
449 SkAutoMutexAcquire ac(mutex_);
450
reed@google.comb1c65b62013-02-26 15:50:51 +0000451 FcPattern* pattern = FcPatternCreate();
452
reed@google.comee619a02013-02-26 22:58:09 +0000453 if (familyName) {
454 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
455 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000456 FcPatternAddInteger(pattern, FC_WEIGHT,
457 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
458 : FC_WEIGHT_NORMAL);
459 FcPatternAddInteger(pattern, FC_SLANT,
460 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
461 : FC_SLANT_ROMAN);
462 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
463
464 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
465 FcDefaultSubstitute(pattern);
466
467 // Font matching:
468 // CSS often specifies a fallback list of families:
469 // font-family: a, b, c, serif;
470 // However, fontconfig will always do its best to find *a* font when asked
471 // for something so we need a way to tell if the match which it has found is
472 // "good enough" for us. Otherwise, we can return NULL which gets piped up
473 // and lets WebKit know to try the next CSS family name. However, fontconfig
474 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
475 // wish to support that.
476 //
477 // Thus, if a specific family is requested we set @family_requested. Then we
478 // record two strings: the family name after config processing and the
479 // family name after resolving. If the two are equal, it's a good match.
480 //
481 // So consider the case where a user has mapped Arial to Helvetica in their
482 // config.
483 // requested family: "Arial"
484 // post_config_family: "Helvetica"
485 // post_match_family: "Helvetica"
486 // -> good match
487 //
488 // and for a missing font:
489 // requested family: "Monaco"
490 // post_config_family: "Monaco"
491 // post_match_family: "Times New Roman"
492 // -> BAD match
493 //
494 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000495
496 const char* post_config_family = get_name(pattern, FC_FAMILY);
497 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000498 // we can just continue with an empty name, e.g. default font
499 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000500 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000501
502 FcResult result;
503 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
504 if (!font_set) {
505 FcPatternDestroy(pattern);
506 return false;
507 }
508
reed@google.comee619a02013-02-26 22:58:09 +0000509 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000510 if (!match) {
511 FcPatternDestroy(pattern);
512 FcFontSetDestroy(font_set);
513 return false;
514 }
515
516 FcPatternDestroy(pattern);
517
reed@google.comf71a2332013-02-27 19:06:30 +0000518 // From here out we just extract our results from 'match'
519
reed@google.come49d67e2013-04-22 18:00:06 +0000520 post_config_family = get_name(match, FC_FAMILY);
521 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000522 FcFontSetDestroy(font_set);
523 return false;
524 }
525
reed@google.come49d67e2013-04-22 18:00:06 +0000526 const char* c_filename = get_name(match, FC_FILE);
527 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000528 FcFontSetDestroy(font_set);
529 return false;
530 }
reed@google.comf71a2332013-02-27 19:06:30 +0000531
reed@google.comb1c65b62013-02-26 15:50:51 +0000532 int face_index;
533 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
534 FcFontSetDestroy(font_set);
535 return false;
536 }
537
reed@google.comb1c65b62013-02-26 15:50:51 +0000538 FcFontSetDestroy(font_set);
539
reed@google.comf71a2332013-02-27 19:06:30 +0000540 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000541 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000542 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000543 }
reed@google.comf71a2332013-02-27 19:06:30 +0000544 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000545 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000546 }
reed@google.comf71a2332013-02-27 19:06:30 +0000547 if (outStyle) {
548 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000549 }
reed@google.comee619a02013-02-26 22:58:09 +0000550 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000551}
552
reed@google.comf71a2332013-02-27 19:06:30 +0000553SkStream* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000554 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000555}
reed@google.com027fd202013-04-19 20:45:30 +0000556
557///////////////////////////////////////////////////////////////////////////////
558
reed@google.com027fd202013-04-19 20:45:30 +0000559static bool find_name(const SkTDArray<const char*>& list, const char* str) {
560 int count = list.count();
561 for (int i = 0; i < count; ++i) {
562 if (!strcmp(list[i], str)) {
563 return true;
564 }
565 }
566 return false;
567}
568
569SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000570 SkAutoMutexAcquire ac(mutex_);
571
reed@google.com027fd202013-04-19 20:45:30 +0000572 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000573 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
574 if (NULL == pat) {
575 return NULL;
576 }
577
578 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
579 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
reed@google.com027fd202013-04-19 20:45:30 +0000580 if (NULL == os) {
reed@google.comdf798b32013-04-19 20:52:50 +0000581 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000582 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000583
reed@google.com027fd202013-04-19 20:45:30 +0000584 FcFontSet* fs = FcFontList(NULL, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000585 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
reed@google.com027fd202013-04-19 20:45:30 +0000586 if (NULL == fs) {
reed@google.comdf798b32013-04-19 20:52:50 +0000587 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000588 }
589
590 SkTDArray<const char*> names;
591 SkTDArray<size_t> sizes;
592 for (int i = 0; i < fs->nfont; ++i) {
593 FcPattern* match = fs->fonts[i];
594 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000595 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000596 *names.append() = famName;
597 *sizes.append() = strlen(famName) + 1;
598 }
599 }
600
reed@google.com027fd202013-04-19 20:45:30 +0000601 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
602 sizes.begin(), names.count());
603}
604
605bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
606 SkString* outFamilyName,
607 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000608 SkAutoMutexAcquire ac(mutex_);
609
610#if 0
611 std::string familyStr(familyName ? familyName : "");
612 if (familyStr.length() > kMaxFontFamilyLength) {
613 return false;
614 }
615
616 SkAutoMutexAcquire ac(mutex_);
617
618 FcPattern* pattern = FcPatternCreate();
619
620 if (familyName) {
621 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
622 }
623 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
624
625 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
626 FcDefaultSubstitute(pattern);
627
628 // Font matching:
629 // CSS often specifies a fallback list of families:
630 // font-family: a, b, c, serif;
631 // However, fontconfig will always do its best to find *a* font when asked
632 // for something so we need a way to tell if the match which it has found is
633 // "good enough" for us. Otherwise, we can return NULL which gets piped up
634 // and lets WebKit know to try the next CSS family name. However, fontconfig
635 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
636 // wish to support that.
637 //
638 // Thus, if a specific family is requested we set @family_requested. Then we
639 // record two strings: the family name after config processing and the
640 // family name after resolving. If the two are equal, it's a good match.
641 //
642 // So consider the case where a user has mapped Arial to Helvetica in their
643 // config.
644 // requested family: "Arial"
645 // post_config_family: "Helvetica"
646 // post_match_family: "Helvetica"
647 // -> good match
648 //
649 // and for a missing font:
650 // requested family: "Monaco"
651 // post_config_family: "Monaco"
652 // post_match_family: "Times New Roman"
653 // -> BAD match
654 //
655 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
656
657 const char* post_config_family = get_name(pattern, FC_FAMILY);
658
659 FcResult result;
660 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
661 if (!font_set) {
662 FcPatternDestroy(pattern);
663 return false;
664 }
665
666 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
667 if (!match) {
668 FcPatternDestroy(pattern);
669 FcFontSetDestroy(font_set);
670 return false;
671 }
672
673 FcPatternDestroy(pattern);
674
675 // From here out we just extract our results from 'match'
676
677 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
678 FcFontSetDestroy(font_set);
679 return false;
680 }
681
682 FcChar8* c_filename;
683 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
684 FcFontSetDestroy(font_set);
685 return false;
686 }
687
688 int face_index;
689 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
690 FcFontSetDestroy(font_set);
691 return false;
692 }
693
694 FcFontSetDestroy(font_set);
695
696 if (outIdentity) {
697 outIdentity->fTTCIndex = face_index;
698 outIdentity->fString.set((const char*)c_filename);
699 }
700 if (outFamilyName) {
701 outFamilyName->set((const char*)post_config_family);
702 }
703 if (outStyle) {
704 *outStyle = GetFontStyle(match);
705 }
706 return true;
707
708////////////////////
709
710 int count;
711 FcPattern** match = MatchFont(font_set, post_config_family, &count);
712 if (!match) {
713 FcPatternDestroy(pattern);
714 FcFontSetDestroy(font_set);
715 return NULL;
716 }
717
718 FcPatternDestroy(pattern);
719
720 SkTDArray<FcPattern*> trimmedMatches;
721 for (int i = 0; i < count; ++i) {
722 const char* justName = find_just_name(get_name(match[i], FC_FILE));
723 if (!is_lower(*justName)) {
724 *trimmedMatches.append() = match[i];
725 }
726 }
727
728 SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
729 (trimmedMatches.begin(),
730 trimmedMatches.count()));
731#endif
reed@google.com027fd202013-04-19 20:45:30 +0000732 return false;
733}