blob: 8589cc2421177f6fa6171a42ff8579d49a66fb1c [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 <unistd.h>
11#include <fcntl.h>
12
13#include <fontconfig/fontconfig.h>
14
reed@google.comf55061f2013-04-22 18:48:45 +000015#include "SkBuffer.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000016#include "SkFontConfigInterface.h"
mtklein78358bf2014-06-02 08:44:27 -070017#include "SkLazyPtr.h"
mtklein1b249332015-07-07 12:21:21 -070018#include "SkMutex.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000019#include "SkStream.h"
mtkleinba59a672014-08-04 10:18:27 -070020#include "SkString.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000021
reed@google.comf55061f2013-04-22 18:48:45 +000022size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
23 size_t size = sizeof(fID) + sizeof(fTTCIndex);
24 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
25 size += sizeof(int32_t) + fString.size(); // store length+data
26 if (addr) {
27 SkWBuffer buffer(addr, size);
28
29 buffer.write32(fID);
30 buffer.write32(fTTCIndex);
31 buffer.write32(fString.size());
32 buffer.write32(fStyle.weight());
33 buffer.write32(fStyle.width());
34 buffer.write8(fStyle.slant());
35 buffer.write(fString.c_str(), fString.size());
36 buffer.padToAlign4();
37
38 SkASSERT(buffer.pos() == size);
39 }
40 return size;
41}
42
43size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
44 size_t size) {
45 SkRBuffer buffer(addr, size);
46
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000047 (void)buffer.readU32(&fID);
48 (void)buffer.readS32(&fTTCIndex);
49 uint32_t strLen, weight, width;
50 (void)buffer.readU32(&strLen);
51 (void)buffer.readU32(&weight);
52 (void)buffer.readU32(&width);
53 uint8_t u8;
54 (void)buffer.readU8(&u8);
55 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
reed@google.comf55061f2013-04-22 18:48:45 +000056 fStyle = SkFontStyle(weight, width, slant);
57 fString.resize(strLen);
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000058 (void)buffer.read(fString.writable_str(), strLen);
reed@google.comf55061f2013-04-22 18:48:45 +000059 buffer.skipToAlign4();
60
61 return buffer.pos(); // the actual number of bytes read
62}
63
64#ifdef SK_DEBUG
65static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
66 iden->fID = 10;
67 iden->fTTCIndex = 2;
68 iden->fString.set("Hello world");
69 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
70}
71
72static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
73 int initValue) {
74 SkFontConfigInterface::FontIdentity iden1;
75
76 size_t size0 = iden0.writeToMemory(NULL);
77
78 SkAutoMalloc storage(size0);
79 memset(storage.get(), initValue, size0);
80
81 size_t size1 = iden0.writeToMemory(storage.get());
82 SkASSERT(size0 == size1);
83
84 SkASSERT(iden0 != iden1);
85 size_t size2 = iden1.readFromMemory(storage.get(), size1);
86 SkASSERT(size2 == size1);
87 SkASSERT(iden0 == iden1);
88}
89
90static void fontconfiginterface_unittest() {
91 SkFontConfigInterface::FontIdentity iden0, iden1;
92
93 SkASSERT(iden0 == iden1);
94
95 make_iden(&iden0);
96 SkASSERT(iden0 != iden1);
97
98 make_iden(&iden1);
99 SkASSERT(iden0 == iden1);
100
101 test_writeToMemory(iden0, 0);
102 test_writeToMemory(iden0, 0);
103}
104#endif
105
reed@google.comb1c65b62013-02-26 15:50:51 +0000106class SkFontConfigInterfaceDirect : public SkFontConfigInterface {
107public:
108 SkFontConfigInterfaceDirect();
109 virtual ~SkFontConfigInterfaceDirect();
110
reed@google.comf71a2332013-02-27 19:06:30 +0000111 virtual bool matchFamilyName(const char familyName[],
112 SkTypeface::Style requested,
113 FontIdentity* outFontIdentifier,
114 SkString* outFamilyName,
mtklein36352bf2015-03-25 18:17:31 -0700115 SkTypeface::Style* outStyle) override;
116 SkStreamAsset* openStream(const FontIdentity&) override;
reed@google.comb1c65b62013-02-26 15:50:51 +0000117
reed@google.com027fd202013-04-19 20:45:30 +0000118 // new APIs
mtklein36352bf2015-03-25 18:17:31 -0700119 SkDataTable* getFamilyNames() override;
reed@google.com027fd202013-04-19 20:45:30 +0000120 virtual bool matchFamilySet(const char inFamilyName[],
121 SkString* outFamilyName,
mtklein36352bf2015-03-25 18:17:31 -0700122 SkTArray<FontIdentity>*) override;
reed@google.com027fd202013-04-19 20:45:30 +0000123
reed@google.comb1c65b62013-02-26 15:50:51 +0000124private:
125 SkMutex mutex_;
reed@google.comb1c65b62013-02-26 15:50:51 +0000126};
127
tomhudsone438ddb2014-07-01 18:54:41 -0700128SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface(SkBaseMutex* mutex) {
129 SkAutoMutexAcquire ac(mutex);
130 static SkFontConfigInterfaceDirect* singleton = NULL;
131 if (singleton == NULL) {
132 singleton = SkNEW(SkFontConfigInterfaceDirect);
133 }
134 return singleton;
reed@google.comb1c65b62013-02-26 15:50:51 +0000135}
136
reed@google.com54c69142013-04-09 15:54:52 +0000137///////////////////////////////////////////////////////////////////////////////
138
reed@google.come49d67e2013-04-22 18:00:06 +0000139// Returns the string from the pattern, or NULL
140static const char* get_name(FcPattern* pattern, const char field[],
141 int index = 0) {
142 const char* name;
143 if (FcPatternGetString(pattern, field, index,
144 (FcChar8**)&name) != FcResultMatch) {
145 name = NULL;
146 }
147 return name;
148}
149
150///////////////////////////////////////////////////////////////////////////////
151
reed@google.comb1c65b62013-02-26 15:50:51 +0000152namespace {
153
154// Equivalence classes, used to match the Liberation and other fonts
155// with their metric-compatible replacements. See the discussion in
156// GetFontEquivClass().
157enum FontEquivClass
158{
159 OTHER,
160 SANS,
161 SERIF,
162 MONO,
163 SYMBOL,
164 PGOTHIC,
165 GOTHIC,
166 PMINCHO,
167 MINCHO,
168 SIMSUN,
169 NSIMSUN,
170 SIMHEI,
171 PMINGLIU,
172 MINGLIU,
173 PMINGLIUHK,
174 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000175 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000176 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000177};
178
179// Match the font name against a whilelist of fonts, returning the equivalence
180// class.
181FontEquivClass GetFontEquivClass(const char* fontname)
182{
183 // It would be nice for fontconfig to tell us whether a given suggested
184 // replacement is a "strong" match (that is, an equivalent font) or
185 // a "weak" match (that is, fontconfig's next-best attempt at finding a
186 // substitute). However, I played around with the fontconfig API for
187 // a good few hours and could not make it reveal this information.
188 //
189 // So instead, we hardcode. Initially this function emulated
190 // /etc/fonts/conf.d/30-metric-aliases.conf
191 // from my Ubuntu system, but we're better off being very conservative.
192
193 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
194 // Arial, Times New Roman and Courier New with a character repertoire
195 // much larger than Liberation. Note that Cousine is metrically
196 // compatible with Courier New, but the former is sans-serif while
197 // the latter is serif.
198
199
200 struct FontEquivMap {
201 FontEquivClass clazz;
202 const char name[40];
203 };
204
205 static const FontEquivMap kFontEquivMap[] = {
206 { SANS, "Arial" },
207 { SANS, "Arimo" },
208 { SANS, "Liberation Sans" },
209
210 { SERIF, "Times New Roman" },
211 { SERIF, "Tinos" },
212 { SERIF, "Liberation Serif" },
213
214 { MONO, "Courier New" },
215 { MONO, "Cousine" },
216 { MONO, "Liberation Mono" },
217
218 { SYMBOL, "Symbol" },
219 { SYMBOL, "Symbol Neu" },
220
221 // MS Pゴシック
222 { PGOTHIC, "MS PGothic" },
223 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
224 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshin7476cf52014-09-09 12:30:57 -0700225 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000226 { PGOTHIC, "IPAPGothic" },
227 { PGOTHIC, "MotoyaG04Gothic" },
228
229 // MS ゴシック
230 { GOTHIC, "MS Gothic" },
231 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
232 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshindd4e5682015-05-12 12:08:36 -0700233 { GOTHIC, "Noto Sans Mono CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000234 { 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" },
jshin7476cf52014-09-09 12:30:57 -0700265 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000266 { SIMHEI, "MYingHeiGB18030" },
267 { SIMHEI, "MYingHeiB5HK" },
268
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000269 // 新細明體
270 { PMINGLIU, "PMingLiU"},
271 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
272 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000273
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000274 // 細明體
275 { MINGLIU, "MingLiU"},
276 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
277 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000278
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000279 // 新細明體
280 { PMINGLIUHK, "PMingLiU_HKSCS"},
281 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
282 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000283
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000284 // 細明體
285 { MINGLIUHK, "MingLiU_HKSCS"},
286 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
287 { MINGLIUHK, "MSung B5HK"},
288
289 // Cambria
290 { CAMBRIA, "Cambria" },
291 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000292
293 // Calibri
294 { CALIBRI, "Calibri" },
295 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000296 };
297
298 static const size_t kFontCount =
299 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
300
301 // TODO(jungshik): If this loop turns out to be hot, turn
302 // the array to a static (hash)map to speed it up.
303 for (size_t i = 0; i < kFontCount; ++i) {
304 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
305 return kFontEquivMap[i].clazz;
306 }
307 return OTHER;
308}
309
310
311// Return true if |font_a| and |font_b| are visually and at the metrics
312// level interchangeable.
313bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
314{
315 FontEquivClass class_a = GetFontEquivClass(font_a);
316 FontEquivClass class_b = GetFontEquivClass(font_b);
317
318 return class_a != OTHER && class_a == class_b;
319}
320
reed@google.comb1c65b62013-02-26 15:50:51 +0000321// Normally we only return exactly the font asked for. In last-resort
322// cases, the request either doesn't specify a font or is one of the
323// basic font names like "Sans", "Serif" or "Monospace". This function
324// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700325bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000326 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700327 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000328 strcasecmp(family_cstr, "sans") == 0 ||
329 strcasecmp(family_cstr, "serif") == 0 ||
330 strcasecmp(family_cstr, "monospace") == 0;
331}
332
reed@google.come49d67e2013-04-22 18:00:06 +0000333static bool valid_pattern(FcPattern* pattern) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000334#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
reed@google.come49d67e2013-04-22 18:00:06 +0000335 FcBool is_scalable;
336 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
337 || !is_scalable) {
338 return false;
339 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000340#endif
reed@google.come49d67e2013-04-22 18:00:06 +0000341
342 // fontconfig can also return fonts which are unreadable
343 const char* c_filename = get_name(pattern, FC_FILE);
344 if (!c_filename) {
345 return false;
346 }
347 if (access(c_filename, R_OK) != 0) {
348 return false;
349 }
350 return true;
351}
352
reed@google.comb1c65b62013-02-26 15:50:51 +0000353// Find matching font from |font_set| for the given font family.
354FcPattern* MatchFont(FcFontSet* font_set,
reed@google.come49d67e2013-04-22 18:00:06 +0000355 const char* post_config_family,
mtkleinba59a672014-08-04 10:18:27 -0700356 const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000357 // Older versions of fontconfig have a bug where they cannot select
358 // only scalable fonts so we have to manually filter the results.
359 FcPattern* match = NULL;
360 for (int i = 0; i < font_set->nfont; ++i) {
361 FcPattern* current = font_set->fonts[i];
reed@google.come49d67e2013-04-22 18:00:06 +0000362 if (valid_pattern(current)) {
363 match = current;
364 break;
reed@google.comb1c65b62013-02-26 15:50:51 +0000365 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000366 }
367
368 if (match && !IsFallbackFontAllowed(family)) {
369 bool acceptable_substitute = false;
370 for (int id = 0; id < 255; ++id) {
reed@google.come49d67e2013-04-22 18:00:06 +0000371 const char* post_match_family = get_name(match, FC_FAMILY, id);
372 if (!post_match_family)
reed@google.comb1c65b62013-02-26 15:50:51 +0000373 break;
374 acceptable_substitute =
reed@google.come49d67e2013-04-22 18:00:06 +0000375 (strcasecmp(post_config_family, post_match_family) == 0 ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000376 // Workaround for Issue 12530:
377 // requested family: "Bitstream Vera Sans"
378 // post_config_family: "Arial"
379 // post_match_family: "Bitstream Vera Sans"
380 // -> We should treat this case as a good match.
reed@google.come49d67e2013-04-22 18:00:06 +0000381 strcasecmp(family.c_str(), post_match_family) == 0) ||
382 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000383 if (acceptable_substitute)
384 break;
385 }
386 if (!acceptable_substitute)
387 return NULL;
388 }
389
390 return match;
391}
392
393// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000394SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000395 int resulting_bold;
396 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
397 resulting_bold = FC_WEIGHT_NORMAL;
398
399 int resulting_italic;
400 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
401 resulting_italic = FC_SLANT_ROMAN;
402
403 // If we ask for an italic font, fontconfig might take a roman font and set
404 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
405 // that the font is italic or oblique. So, if we see a matrix, we don't
406 // believe that it's italic.
407 FcValue matrix;
408 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
409
410 // If we ask for an italic font, fontconfig might take a roman font and set
411 // FC_EMBOLDEN.
412 FcValue embolden;
413 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
414
415 int styleBits = 0;
416 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
417 styleBits |= SkTypeface::kBold;
418 }
419 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
420 styleBits |= SkTypeface::kItalic;
421 }
422
reed@google.comf71a2332013-02-27 19:06:30 +0000423 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000424}
425
426} // anonymous namespace
427
428///////////////////////////////////////////////////////////////////////////////
429
reed@google.comf71a2332013-02-27 19:06:30 +0000430#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000431
reed@google.comf71a2332013-02-27 19:06:30 +0000432SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000433 SkAutoMutexAcquire ac(mutex_);
434
reed@google.comb1c65b62013-02-26 15:50:51 +0000435 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000436
437 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000438}
439
440SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
441}
442
reed@google.comf71a2332013-02-27 19:06:30 +0000443bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
444 SkTypeface::Style style,
445 FontIdentity* outIdentity,
446 SkString* outFamilyName,
447 SkTypeface::Style* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700448 SkString familyStr(familyName ? familyName : "");
449 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000450 return false;
451 }
452
453 SkAutoMutexAcquire ac(mutex_);
454
reed@google.comb1c65b62013-02-26 15:50:51 +0000455 FcPattern* pattern = FcPatternCreate();
456
reed@google.comee619a02013-02-26 22:58:09 +0000457 if (familyName) {
458 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
459 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000460 FcPatternAddInteger(pattern, FC_WEIGHT,
461 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
462 : FC_WEIGHT_NORMAL);
463 FcPatternAddInteger(pattern, FC_SLANT,
464 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
465 : FC_SLANT_ROMAN);
466 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
467
468 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
469 FcDefaultSubstitute(pattern);
470
471 // Font matching:
472 // CSS often specifies a fallback list of families:
473 // font-family: a, b, c, serif;
474 // However, fontconfig will always do its best to find *a* font when asked
475 // for something so we need a way to tell if the match which it has found is
476 // "good enough" for us. Otherwise, we can return NULL which gets piped up
477 // and lets WebKit know to try the next CSS family name. However, fontconfig
478 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
479 // wish to support that.
480 //
481 // Thus, if a specific family is requested we set @family_requested. Then we
482 // record two strings: the family name after config processing and the
483 // family name after resolving. If the two are equal, it's a good match.
484 //
485 // So consider the case where a user has mapped Arial to Helvetica in their
486 // config.
487 // requested family: "Arial"
488 // post_config_family: "Helvetica"
489 // post_match_family: "Helvetica"
490 // -> good match
491 //
492 // and for a missing font:
493 // requested family: "Monaco"
494 // post_config_family: "Monaco"
495 // post_match_family: "Times New Roman"
496 // -> BAD match
497 //
498 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000499
500 const char* post_config_family = get_name(pattern, FC_FAMILY);
501 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000502 // we can just continue with an empty name, e.g. default font
503 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000504 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000505
506 FcResult result;
507 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
508 if (!font_set) {
509 FcPatternDestroy(pattern);
510 return false;
511 }
512
reed@google.comee619a02013-02-26 22:58:09 +0000513 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000514 if (!match) {
515 FcPatternDestroy(pattern);
516 FcFontSetDestroy(font_set);
517 return false;
518 }
519
520 FcPatternDestroy(pattern);
521
reed@google.comf71a2332013-02-27 19:06:30 +0000522 // From here out we just extract our results from 'match'
523
reed@google.come49d67e2013-04-22 18:00:06 +0000524 post_config_family = get_name(match, FC_FAMILY);
525 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000526 FcFontSetDestroy(font_set);
527 return false;
528 }
529
reed@google.come49d67e2013-04-22 18:00:06 +0000530 const char* c_filename = get_name(match, FC_FILE);
531 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000532 FcFontSetDestroy(font_set);
533 return false;
534 }
reed@google.comf71a2332013-02-27 19:06:30 +0000535
reed@google.comb1c65b62013-02-26 15:50:51 +0000536 int face_index;
537 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
538 FcFontSetDestroy(font_set);
539 return false;
540 }
541
reed@google.comb1c65b62013-02-26 15:50:51 +0000542 FcFontSetDestroy(font_set);
543
reed@google.comf71a2332013-02-27 19:06:30 +0000544 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000545 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000546 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000547 }
reed@google.comf71a2332013-02-27 19:06:30 +0000548 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000549 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000550 }
reed@google.comf71a2332013-02-27 19:06:30 +0000551 if (outStyle) {
552 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000553 }
reed@google.comee619a02013-02-26 22:58:09 +0000554 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000555}
556
bungeman5f213d92015-01-27 05:39:10 -0800557SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000558 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000559}
reed@google.com027fd202013-04-19 20:45:30 +0000560
561///////////////////////////////////////////////////////////////////////////////
562
reed@google.com027fd202013-04-19 20:45:30 +0000563static bool find_name(const SkTDArray<const char*>& list, const char* str) {
564 int count = list.count();
565 for (int i = 0; i < count; ++i) {
566 if (!strcmp(list[i], str)) {
567 return true;
568 }
569 }
570 return false;
571}
572
573SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000574 SkAutoMutexAcquire ac(mutex_);
575
reed@google.com027fd202013-04-19 20:45:30 +0000576 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000577 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
578 if (NULL == pat) {
579 return NULL;
580 }
581
582 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
583 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
reed@google.com027fd202013-04-19 20:45:30 +0000584 if (NULL == os) {
reed@google.comdf798b32013-04-19 20:52:50 +0000585 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000586 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000587
reed@google.com027fd202013-04-19 20:45:30 +0000588 FcFontSet* fs = FcFontList(NULL, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000589 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
reed@google.com027fd202013-04-19 20:45:30 +0000590 if (NULL == fs) {
reed@google.comdf798b32013-04-19 20:52:50 +0000591 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000592 }
593
594 SkTDArray<const char*> names;
595 SkTDArray<size_t> sizes;
596 for (int i = 0; i < fs->nfont; ++i) {
597 FcPattern* match = fs->fonts[i];
598 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000599 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000600 *names.append() = famName;
601 *sizes.append() = strlen(famName) + 1;
602 }
603 }
604
reed@google.com027fd202013-04-19 20:45:30 +0000605 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
606 sizes.begin(), names.count());
607}
608
609bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
610 SkString* outFamilyName,
611 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000612 SkAutoMutexAcquire ac(mutex_);
613
614#if 0
mtkleinba59a672014-08-04 10:18:27 -0700615 SkString familyStr(familyName ? familyName : "");
616 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comf55061f2013-04-22 18:48:45 +0000617 return false;
618 }
619
620 SkAutoMutexAcquire ac(mutex_);
621
622 FcPattern* pattern = FcPatternCreate();
623
624 if (familyName) {
625 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
626 }
627 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
628
629 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
630 FcDefaultSubstitute(pattern);
631
632 // Font matching:
633 // CSS often specifies a fallback list of families:
634 // font-family: a, b, c, serif;
635 // However, fontconfig will always do its best to find *a* font when asked
636 // for something so we need a way to tell if the match which it has found is
637 // "good enough" for us. Otherwise, we can return NULL which gets piped up
638 // and lets WebKit know to try the next CSS family name. However, fontconfig
639 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
640 // wish to support that.
641 //
642 // Thus, if a specific family is requested we set @family_requested. Then we
643 // record two strings: the family name after config processing and the
644 // family name after resolving. If the two are equal, it's a good match.
645 //
646 // So consider the case where a user has mapped Arial to Helvetica in their
647 // config.
648 // requested family: "Arial"
649 // post_config_family: "Helvetica"
650 // post_match_family: "Helvetica"
651 // -> good match
652 //
653 // and for a missing font:
654 // requested family: "Monaco"
655 // post_config_family: "Monaco"
656 // post_match_family: "Times New Roman"
657 // -> BAD match
658 //
659 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
660
661 const char* post_config_family = get_name(pattern, FC_FAMILY);
662
663 FcResult result;
664 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
665 if (!font_set) {
666 FcPatternDestroy(pattern);
667 return false;
668 }
669
670 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
671 if (!match) {
672 FcPatternDestroy(pattern);
673 FcFontSetDestroy(font_set);
674 return false;
675 }
676
677 FcPatternDestroy(pattern);
678
679 // From here out we just extract our results from 'match'
680
681 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
682 FcFontSetDestroy(font_set);
683 return false;
684 }
685
686 FcChar8* c_filename;
687 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
688 FcFontSetDestroy(font_set);
689 return false;
690 }
691
692 int face_index;
693 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
694 FcFontSetDestroy(font_set);
695 return false;
696 }
697
698 FcFontSetDestroy(font_set);
699
700 if (outIdentity) {
701 outIdentity->fTTCIndex = face_index;
702 outIdentity->fString.set((const char*)c_filename);
703 }
704 if (outFamilyName) {
705 outFamilyName->set((const char*)post_config_family);
706 }
707 if (outStyle) {
708 *outStyle = GetFontStyle(match);
709 }
710 return true;
711
712////////////////////
713
714 int count;
715 FcPattern** match = MatchFont(font_set, post_config_family, &count);
716 if (!match) {
717 FcPatternDestroy(pattern);
718 FcFontSetDestroy(font_set);
719 return NULL;
720 }
721
722 FcPatternDestroy(pattern);
723
724 SkTDArray<FcPattern*> trimmedMatches;
725 for (int i = 0; i < count; ++i) {
726 const char* justName = find_just_name(get_name(match[i], FC_FILE));
727 if (!is_lower(*justName)) {
728 *trimmedMatches.append() = match[i];
729 }
730 }
731
732 SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
733 (trimmedMatches.begin(),
734 trimmedMatches.count()));
735#endif
reed@google.com027fd202013-04-19 20:45:30 +0000736 return false;
737}