blob: 13993f10c0d54d4b8345a810b9376bd6e800e371 [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) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000333#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
reed@google.come49d67e2013-04-22 18:00:06 +0000334 FcBool is_scalable;
335 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
336 || !is_scalable) {
337 return false;
338 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000339#endif
reed@google.come49d67e2013-04-22 18:00:06 +0000340
341 // fontconfig can also return fonts which are unreadable
342 const char* c_filename = get_name(pattern, FC_FILE);
343 if (!c_filename) {
344 return false;
345 }
346 if (access(c_filename, R_OK) != 0) {
347 return false;
348 }
349 return true;
350}
351
reed@google.comb1c65b62013-02-26 15:50:51 +0000352// Find matching font from |font_set| for the given font family.
353FcPattern* MatchFont(FcFontSet* font_set,
reed@google.come49d67e2013-04-22 18:00:06 +0000354 const char* post_config_family,
reed@google.comb1c65b62013-02-26 15:50:51 +0000355 const std::string& family) {
356 // Older versions of fontconfig have a bug where they cannot select
357 // only scalable fonts so we have to manually filter the results.
358 FcPattern* match = NULL;
359 for (int i = 0; i < font_set->nfont; ++i) {
360 FcPattern* current = font_set->fonts[i];
reed@google.come49d67e2013-04-22 18:00:06 +0000361 if (valid_pattern(current)) {
362 match = current;
363 break;
reed@google.comb1c65b62013-02-26 15:50:51 +0000364 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000365 }
366
367 if (match && !IsFallbackFontAllowed(family)) {
368 bool acceptable_substitute = false;
369 for (int id = 0; id < 255; ++id) {
reed@google.come49d67e2013-04-22 18:00:06 +0000370 const char* post_match_family = get_name(match, FC_FAMILY, id);
371 if (!post_match_family)
reed@google.comb1c65b62013-02-26 15:50:51 +0000372 break;
373 acceptable_substitute =
reed@google.come49d67e2013-04-22 18:00:06 +0000374 (strcasecmp(post_config_family, post_match_family) == 0 ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000375 // Workaround for Issue 12530:
376 // requested family: "Bitstream Vera Sans"
377 // post_config_family: "Arial"
378 // post_match_family: "Bitstream Vera Sans"
379 // -> We should treat this case as a good match.
reed@google.come49d67e2013-04-22 18:00:06 +0000380 strcasecmp(family.c_str(), post_match_family) == 0) ||
381 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000382 if (acceptable_substitute)
383 break;
384 }
385 if (!acceptable_substitute)
386 return NULL;
387 }
388
389 return match;
390}
391
392// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000393SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000394 int resulting_bold;
395 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
396 resulting_bold = FC_WEIGHT_NORMAL;
397
398 int resulting_italic;
399 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
400 resulting_italic = FC_SLANT_ROMAN;
401
402 // If we ask for an italic font, fontconfig might take a roman font and set
403 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
404 // that the font is italic or oblique. So, if we see a matrix, we don't
405 // believe that it's italic.
406 FcValue matrix;
407 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
408
409 // If we ask for an italic font, fontconfig might take a roman font and set
410 // FC_EMBOLDEN.
411 FcValue embolden;
412 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
413
414 int styleBits = 0;
415 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
416 styleBits |= SkTypeface::kBold;
417 }
418 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
419 styleBits |= SkTypeface::kItalic;
420 }
421
reed@google.comf71a2332013-02-27 19:06:30 +0000422 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000423}
424
425} // anonymous namespace
426
427///////////////////////////////////////////////////////////////////////////////
428
reed@google.comf71a2332013-02-27 19:06:30 +0000429#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000430
reed@google.comf71a2332013-02-27 19:06:30 +0000431SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000432 SkAutoMutexAcquire ac(mutex_);
433
reed@google.comb1c65b62013-02-26 15:50:51 +0000434 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000435
436 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000437}
438
439SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
440}
441
reed@google.comf71a2332013-02-27 19:06:30 +0000442bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
443 SkTypeface::Style style,
444 FontIdentity* outIdentity,
445 SkString* outFamilyName,
446 SkTypeface::Style* outStyle) {
reed@google.comee619a02013-02-26 22:58:09 +0000447 std::string familyStr(familyName ? familyName : "");
448 if (familyStr.length() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000449 return false;
450 }
451
452 SkAutoMutexAcquire ac(mutex_);
453
reed@google.comb1c65b62013-02-26 15:50:51 +0000454 FcPattern* pattern = FcPatternCreate();
455
reed@google.comee619a02013-02-26 22:58:09 +0000456 if (familyName) {
457 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
458 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000459 FcPatternAddInteger(pattern, FC_WEIGHT,
460 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
461 : FC_WEIGHT_NORMAL);
462 FcPatternAddInteger(pattern, FC_SLANT,
463 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
464 : FC_SLANT_ROMAN);
465 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
466
467 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
468 FcDefaultSubstitute(pattern);
469
470 // Font matching:
471 // CSS often specifies a fallback list of families:
472 // font-family: a, b, c, serif;
473 // However, fontconfig will always do its best to find *a* font when asked
474 // for something so we need a way to tell if the match which it has found is
475 // "good enough" for us. Otherwise, we can return NULL which gets piped up
476 // and lets WebKit know to try the next CSS family name. However, fontconfig
477 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
478 // wish to support that.
479 //
480 // Thus, if a specific family is requested we set @family_requested. Then we
481 // record two strings: the family name after config processing and the
482 // family name after resolving. If the two are equal, it's a good match.
483 //
484 // So consider the case where a user has mapped Arial to Helvetica in their
485 // config.
486 // requested family: "Arial"
487 // post_config_family: "Helvetica"
488 // post_match_family: "Helvetica"
489 // -> good match
490 //
491 // and for a missing font:
492 // requested family: "Monaco"
493 // post_config_family: "Monaco"
494 // post_match_family: "Times New Roman"
495 // -> BAD match
496 //
497 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000498
499 const char* post_config_family = get_name(pattern, FC_FAMILY);
500 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000501 // we can just continue with an empty name, e.g. default font
502 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000503 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000504
505 FcResult result;
506 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
507 if (!font_set) {
508 FcPatternDestroy(pattern);
509 return false;
510 }
511
reed@google.comee619a02013-02-26 22:58:09 +0000512 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000513 if (!match) {
514 FcPatternDestroy(pattern);
515 FcFontSetDestroy(font_set);
516 return false;
517 }
518
519 FcPatternDestroy(pattern);
520
reed@google.comf71a2332013-02-27 19:06:30 +0000521 // From here out we just extract our results from 'match'
522
reed@google.come49d67e2013-04-22 18:00:06 +0000523 post_config_family = get_name(match, FC_FAMILY);
524 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000525 FcFontSetDestroy(font_set);
526 return false;
527 }
528
reed@google.come49d67e2013-04-22 18:00:06 +0000529 const char* c_filename = get_name(match, FC_FILE);
530 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000531 FcFontSetDestroy(font_set);
532 return false;
533 }
reed@google.comf71a2332013-02-27 19:06:30 +0000534
reed@google.comb1c65b62013-02-26 15:50:51 +0000535 int face_index;
536 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
537 FcFontSetDestroy(font_set);
538 return false;
539 }
540
reed@google.comb1c65b62013-02-26 15:50:51 +0000541 FcFontSetDestroy(font_set);
542
reed@google.comf71a2332013-02-27 19:06:30 +0000543 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000544 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000545 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000546 }
reed@google.comf71a2332013-02-27 19:06:30 +0000547 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000548 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000549 }
reed@google.comf71a2332013-02-27 19:06:30 +0000550 if (outStyle) {
551 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000552 }
reed@google.comee619a02013-02-26 22:58:09 +0000553 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000554}
555
reed@google.comf71a2332013-02-27 19:06:30 +0000556SkStream* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000557 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000558}
reed@google.com027fd202013-04-19 20:45:30 +0000559
560///////////////////////////////////////////////////////////////////////////////
561
reed@google.com027fd202013-04-19 20:45:30 +0000562static bool find_name(const SkTDArray<const char*>& list, const char* str) {
563 int count = list.count();
564 for (int i = 0; i < count; ++i) {
565 if (!strcmp(list[i], str)) {
566 return true;
567 }
568 }
569 return false;
570}
571
572SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000573 SkAutoMutexAcquire ac(mutex_);
574
reed@google.com027fd202013-04-19 20:45:30 +0000575 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000576 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
577 if (NULL == pat) {
578 return NULL;
579 }
580
581 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
582 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
reed@google.com027fd202013-04-19 20:45:30 +0000583 if (NULL == os) {
reed@google.comdf798b32013-04-19 20:52:50 +0000584 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000585 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000586
reed@google.com027fd202013-04-19 20:45:30 +0000587 FcFontSet* fs = FcFontList(NULL, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000588 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
reed@google.com027fd202013-04-19 20:45:30 +0000589 if (NULL == fs) {
reed@google.comdf798b32013-04-19 20:52:50 +0000590 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000591 }
592
593 SkTDArray<const char*> names;
594 SkTDArray<size_t> sizes;
595 for (int i = 0; i < fs->nfont; ++i) {
596 FcPattern* match = fs->fonts[i];
597 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000598 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000599 *names.append() = famName;
600 *sizes.append() = strlen(famName) + 1;
601 }
602 }
603
reed@google.com027fd202013-04-19 20:45:30 +0000604 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
605 sizes.begin(), names.count());
606}
607
608bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
609 SkString* outFamilyName,
610 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000611 SkAutoMutexAcquire ac(mutex_);
612
613#if 0
614 std::string familyStr(familyName ? familyName : "");
615 if (familyStr.length() > kMaxFontFamilyLength) {
616 return false;
617 }
618
619 SkAutoMutexAcquire ac(mutex_);
620
621 FcPattern* pattern = FcPatternCreate();
622
623 if (familyName) {
624 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
625 }
626 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
627
628 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
629 FcDefaultSubstitute(pattern);
630
631 // Font matching:
632 // CSS often specifies a fallback list of families:
633 // font-family: a, b, c, serif;
634 // However, fontconfig will always do its best to find *a* font when asked
635 // for something so we need a way to tell if the match which it has found is
636 // "good enough" for us. Otherwise, we can return NULL which gets piped up
637 // and lets WebKit know to try the next CSS family name. However, fontconfig
638 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
639 // wish to support that.
640 //
641 // Thus, if a specific family is requested we set @family_requested. Then we
642 // record two strings: the family name after config processing and the
643 // family name after resolving. If the two are equal, it's a good match.
644 //
645 // So consider the case where a user has mapped Arial to Helvetica in their
646 // config.
647 // requested family: "Arial"
648 // post_config_family: "Helvetica"
649 // post_match_family: "Helvetica"
650 // -> good match
651 //
652 // and for a missing font:
653 // requested family: "Monaco"
654 // post_config_family: "Monaco"
655 // post_match_family: "Times New Roman"
656 // -> BAD match
657 //
658 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
659
660 const char* post_config_family = get_name(pattern, FC_FAMILY);
661
662 FcResult result;
663 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
664 if (!font_set) {
665 FcPatternDestroy(pattern);
666 return false;
667 }
668
669 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
670 if (!match) {
671 FcPatternDestroy(pattern);
672 FcFontSetDestroy(font_set);
673 return false;
674 }
675
676 FcPatternDestroy(pattern);
677
678 // From here out we just extract our results from 'match'
679
680 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
681 FcFontSetDestroy(font_set);
682 return false;
683 }
684
685 FcChar8* c_filename;
686 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
687 FcFontSetDestroy(font_set);
688 return false;
689 }
690
691 int face_index;
692 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
693 FcFontSetDestroy(font_set);
694 return false;
695 }
696
697 FcFontSetDestroy(font_set);
698
699 if (outIdentity) {
700 outIdentity->fTTCIndex = face_index;
701 outIdentity->fString.set((const char*)c_filename);
702 }
703 if (outFamilyName) {
704 outFamilyName->set((const char*)post_config_family);
705 }
706 if (outStyle) {
707 *outStyle = GetFontStyle(match);
708 }
709 return true;
710
711////////////////////
712
713 int count;
714 FcPattern** match = MatchFont(font_set, post_config_family, &count);
715 if (!match) {
716 FcPatternDestroy(pattern);
717 FcFontSetDestroy(font_set);
718 return NULL;
719 }
720
721 FcPatternDestroy(pattern);
722
723 SkTDArray<FcPattern*> trimmedMatches;
724 for (int i = 0; i < count; ++i) {
725 const char* justName = find_just_name(get_name(match[i], FC_FILE));
726 if (!is_lower(*justName)) {
727 *trimmedMatches.append() = match[i];
728 }
729 }
730
731 SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
732 (trimmedMatches.begin(),
733 trimmedMatches.count()));
734#endif
reed@google.com027fd202013-04-19 20:45:30 +0000735 return false;
736}