blob: a25bfbdc07a8cc9875bc4858f16c9c08b894dd1e [file] [log] [blame]
reed@google.comb1c65b62013-02-26 15:50:51 +00001/*
benjaminwagner2211a7b2015-12-01 11:12:05 -08002 * Copyright 2009-2015 Google Inc.
reed@google.comb1c65b62013-02-26 15:50:51 +00003 *
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.comf55061f2013-04-22 18:48:45 +000010#include "SkBuffer.h"
bungemanf20488b2015-07-29 11:49:40 -070011#include "SkDataTable.h"
benjaminwagner2211a7b2015-12-01 11:12:05 -080012#include "SkFontConfigInterface_direct.h"
bungemanf20488b2015-07-29 11:49:40 -070013#include "SkFontStyle.h"
mtklein1b249332015-07-07 12:21:21 -070014#include "SkMutex.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000015#include "SkStream.h"
mtkleinba59a672014-08-04 10:18:27 -070016#include "SkString.h"
bungemanf20488b2015-07-29 11:49:40 -070017#include "SkTArray.h"
18#include "SkTDArray.h"
19#include "SkTemplates.h"
20#include "SkTypeface.h"
21#include "SkTypes.h"
22
23#include <fontconfig/fontconfig.h>
24#include <unistd.h>
reed@google.comb1c65b62013-02-26 15:50:51 +000025
reed@google.comf55061f2013-04-22 18:48:45 +000026size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
27 size_t size = sizeof(fID) + sizeof(fTTCIndex);
28 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
29 size += sizeof(int32_t) + fString.size(); // store length+data
30 if (addr) {
31 SkWBuffer buffer(addr, size);
32
33 buffer.write32(fID);
34 buffer.write32(fTTCIndex);
35 buffer.write32(fString.size());
36 buffer.write32(fStyle.weight());
37 buffer.write32(fStyle.width());
38 buffer.write8(fStyle.slant());
39 buffer.write(fString.c_str(), fString.size());
40 buffer.padToAlign4();
41
42 SkASSERT(buffer.pos() == size);
43 }
44 return size;
45}
46
47size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
48 size_t size) {
49 SkRBuffer buffer(addr, size);
50
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000051 (void)buffer.readU32(&fID);
52 (void)buffer.readS32(&fTTCIndex);
53 uint32_t strLen, weight, width;
54 (void)buffer.readU32(&strLen);
55 (void)buffer.readU32(&weight);
56 (void)buffer.readU32(&width);
57 uint8_t u8;
58 (void)buffer.readU8(&u8);
59 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
reed@google.comf55061f2013-04-22 18:48:45 +000060 fStyle = SkFontStyle(weight, width, slant);
61 fString.resize(strLen);
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000062 (void)buffer.read(fString.writable_str(), strLen);
reed@google.comf55061f2013-04-22 18:48:45 +000063 buffer.skipToAlign4();
64
65 return buffer.pos(); // the actual number of bytes read
66}
67
68#ifdef SK_DEBUG
69static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
70 iden->fID = 10;
71 iden->fTTCIndex = 2;
72 iden->fString.set("Hello world");
73 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
74}
75
76static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
77 int initValue) {
78 SkFontConfigInterface::FontIdentity iden1;
79
halcanary96fcdcc2015-08-27 07:41:13 -070080 size_t size0 = iden0.writeToMemory(nullptr);
reed@google.comf55061f2013-04-22 18:48:45 +000081
82 SkAutoMalloc storage(size0);
83 memset(storage.get(), initValue, size0);
84
85 size_t size1 = iden0.writeToMemory(storage.get());
86 SkASSERT(size0 == size1);
87
88 SkASSERT(iden0 != iden1);
89 size_t size2 = iden1.readFromMemory(storage.get(), size1);
90 SkASSERT(size2 == size1);
91 SkASSERT(iden0 == iden1);
92}
93
94static void fontconfiginterface_unittest() {
95 SkFontConfigInterface::FontIdentity iden0, iden1;
96
97 SkASSERT(iden0 == iden1);
98
99 make_iden(&iden0);
100 SkASSERT(iden0 != iden1);
101
102 make_iden(&iden1);
103 SkASSERT(iden0 == iden1);
104
105 test_writeToMemory(iden0, 0);
106 test_writeToMemory(iden0, 0);
107}
108#endif
109
reed@google.com54c69142013-04-09 15:54:52 +0000110///////////////////////////////////////////////////////////////////////////////
111
halcanary96fcdcc2015-08-27 07:41:13 -0700112// Returns the string from the pattern, or nullptr
reed@google.come49d67e2013-04-22 18:00:06 +0000113static const char* get_name(FcPattern* pattern, const char field[],
114 int index = 0) {
115 const char* name;
116 if (FcPatternGetString(pattern, field, index,
117 (FcChar8**)&name) != FcResultMatch) {
halcanary96fcdcc2015-08-27 07:41:13 -0700118 name = nullptr;
reed@google.come49d67e2013-04-22 18:00:06 +0000119 }
120 return name;
121}
122
123///////////////////////////////////////////////////////////////////////////////
124
reed@google.comb1c65b62013-02-26 15:50:51 +0000125namespace {
126
127// Equivalence classes, used to match the Liberation and other fonts
128// with their metric-compatible replacements. See the discussion in
129// GetFontEquivClass().
130enum FontEquivClass
131{
132 OTHER,
133 SANS,
134 SERIF,
135 MONO,
136 SYMBOL,
137 PGOTHIC,
138 GOTHIC,
139 PMINCHO,
140 MINCHO,
141 SIMSUN,
142 NSIMSUN,
143 SIMHEI,
144 PMINGLIU,
145 MINGLIU,
146 PMINGLIUHK,
147 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000148 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000149 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000150};
151
152// Match the font name against a whilelist of fonts, returning the equivalence
153// class.
154FontEquivClass GetFontEquivClass(const char* fontname)
155{
156 // It would be nice for fontconfig to tell us whether a given suggested
157 // replacement is a "strong" match (that is, an equivalent font) or
158 // a "weak" match (that is, fontconfig's next-best attempt at finding a
159 // substitute). However, I played around with the fontconfig API for
160 // a good few hours and could not make it reveal this information.
161 //
162 // So instead, we hardcode. Initially this function emulated
163 // /etc/fonts/conf.d/30-metric-aliases.conf
164 // from my Ubuntu system, but we're better off being very conservative.
165
166 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
167 // Arial, Times New Roman and Courier New with a character repertoire
168 // much larger than Liberation. Note that Cousine is metrically
169 // compatible with Courier New, but the former is sans-serif while
170 // the latter is serif.
171
172
173 struct FontEquivMap {
174 FontEquivClass clazz;
175 const char name[40];
176 };
177
178 static const FontEquivMap kFontEquivMap[] = {
179 { SANS, "Arial" },
180 { SANS, "Arimo" },
181 { SANS, "Liberation Sans" },
182
183 { SERIF, "Times New Roman" },
184 { SERIF, "Tinos" },
185 { SERIF, "Liberation Serif" },
186
187 { MONO, "Courier New" },
188 { MONO, "Cousine" },
189 { MONO, "Liberation Mono" },
190
191 { SYMBOL, "Symbol" },
192 { SYMBOL, "Symbol Neu" },
193
194 // MS Pゴシック
195 { PGOTHIC, "MS PGothic" },
196 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
197 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshin7476cf52014-09-09 12:30:57 -0700198 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000199 { PGOTHIC, "IPAPGothic" },
200 { PGOTHIC, "MotoyaG04Gothic" },
201
202 // MS ゴシック
203 { GOTHIC, "MS Gothic" },
204 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
205 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshindd4e5682015-05-12 12:08:36 -0700206 { GOTHIC, "Noto Sans Mono CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000207 { GOTHIC, "IPAGothic" },
208 { GOTHIC, "MotoyaG04GothicMono" },
209
210 // MS P明朝
211 { PMINCHO, "MS PMincho" },
212 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
213 "\xe6\x98\x8e\xe6\x9c\x9d"},
214 { PMINCHO, "IPAPMincho" },
215 { PMINCHO, "MotoyaG04Mincho" },
216
217 // MS 明朝
218 { MINCHO, "MS Mincho" },
219 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
220 { MINCHO, "IPAMincho" },
221 { MINCHO, "MotoyaG04MinchoMono" },
222
223 // 宋体
224 { SIMSUN, "Simsun" },
225 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
226 { SIMSUN, "MSung GB18030" },
227 { SIMSUN, "Song ASC" },
228
229 // 新宋体
230 { NSIMSUN, "NSimsun" },
231 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
232 { NSIMSUN, "MSung GB18030" },
233 { NSIMSUN, "N Song ASC" },
234
235 // 黑体
236 { SIMHEI, "Simhei" },
237 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700238 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000239 { SIMHEI, "MYingHeiGB18030" },
240 { SIMHEI, "MYingHeiB5HK" },
241
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000242 // 新細明體
243 { PMINGLIU, "PMingLiU"},
244 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
245 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000246
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000247 // 細明體
248 { MINGLIU, "MingLiU"},
249 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
250 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000251
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000252 // 新細明體
253 { PMINGLIUHK, "PMingLiU_HKSCS"},
254 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
255 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000256
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000257 // 細明體
258 { MINGLIUHK, "MingLiU_HKSCS"},
259 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
260 { MINGLIUHK, "MSung B5HK"},
261
262 // Cambria
263 { CAMBRIA, "Cambria" },
264 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000265
266 // Calibri
267 { CALIBRI, "Calibri" },
268 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000269 };
270
271 static const size_t kFontCount =
272 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
273
274 // TODO(jungshik): If this loop turns out to be hot, turn
275 // the array to a static (hash)map to speed it up.
276 for (size_t i = 0; i < kFontCount; ++i) {
277 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
278 return kFontEquivMap[i].clazz;
279 }
280 return OTHER;
281}
282
283
284// Return true if |font_a| and |font_b| are visually and at the metrics
285// level interchangeable.
286bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
287{
288 FontEquivClass class_a = GetFontEquivClass(font_a);
289 FontEquivClass class_b = GetFontEquivClass(font_b);
290
291 return class_a != OTHER && class_a == class_b;
292}
293
reed@google.comb1c65b62013-02-26 15:50:51 +0000294// Normally we only return exactly the font asked for. In last-resort
295// cases, the request either doesn't specify a font or is one of the
296// basic font names like "Sans", "Serif" or "Monospace". This function
297// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700298bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000299 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700300 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000301 strcasecmp(family_cstr, "sans") == 0 ||
302 strcasecmp(family_cstr, "serif") == 0 ||
303 strcasecmp(family_cstr, "monospace") == 0;
304}
305
reed@google.comb1c65b62013-02-26 15:50:51 +0000306// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000307SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000308 int resulting_bold;
309 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
310 resulting_bold = FC_WEIGHT_NORMAL;
311
312 int resulting_italic;
313 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
314 resulting_italic = FC_SLANT_ROMAN;
315
316 // If we ask for an italic font, fontconfig might take a roman font and set
317 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
318 // that the font is italic or oblique. So, if we see a matrix, we don't
319 // believe that it's italic.
320 FcValue matrix;
321 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
322
323 // If we ask for an italic font, fontconfig might take a roman font and set
324 // FC_EMBOLDEN.
325 FcValue embolden;
326 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
327
328 int styleBits = 0;
329 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
330 styleBits |= SkTypeface::kBold;
331 }
332 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
333 styleBits |= SkTypeface::kItalic;
334 }
335
reed@google.comf71a2332013-02-27 19:06:30 +0000336 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000337}
338
339} // anonymous namespace
340
341///////////////////////////////////////////////////////////////////////////////
342
reed@google.comf71a2332013-02-27 19:06:30 +0000343#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000344
reed@google.comf71a2332013-02-27 19:06:30 +0000345SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000346 SkAutoMutexAcquire ac(mutex_);
347
reed@google.comb1c65b62013-02-26 15:50:51 +0000348 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000349
350 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000351}
352
353SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
354}
355
benjaminwagner2211a7b2015-12-01 11:12:05 -0800356bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
357 if (access(filename, R_OK) != 0) {
358 return false;
359 }
360 return true;
361}
362
363bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
364#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
365 FcBool is_scalable;
366 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
367 || !is_scalable) {
368 return false;
369 }
370#endif
371
372 // fontconfig can also return fonts which are unreadable
373 const char* c_filename = get_name(pattern, FC_FILE);
374 if (!c_filename) {
375 return false;
376 }
377 return this->isAccessible(c_filename);
378}
379
380// Find matching font from |font_set| for the given font family.
381FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
382 const char* post_config_family,
383 const SkString& family) {
384 // Older versions of fontconfig have a bug where they cannot select
385 // only scalable fonts so we have to manually filter the results.
386 FcPattern* match = nullptr;
387 for (int i = 0; i < font_set->nfont; ++i) {
388 FcPattern* current = font_set->fonts[i];
389 if (this->isValidPattern(current)) {
390 match = current;
391 break;
392 }
393 }
394
395 if (match && !IsFallbackFontAllowed(family)) {
396 bool acceptable_substitute = false;
397 for (int id = 0; id < 255; ++id) {
398 const char* post_match_family = get_name(match, FC_FAMILY, id);
399 if (!post_match_family)
400 break;
401 acceptable_substitute =
402 (strcasecmp(post_config_family, post_match_family) == 0 ||
403 // Workaround for Issue 12530:
404 // requested family: "Bitstream Vera Sans"
405 // post_config_family: "Arial"
406 // post_match_family: "Bitstream Vera Sans"
407 // -> We should treat this case as a good match.
408 strcasecmp(family.c_str(), post_match_family) == 0) ||
409 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
410 if (acceptable_substitute)
411 break;
412 }
413 if (!acceptable_substitute)
414 return nullptr;
415 }
416
417 return match;
418}
419
reed@google.comf71a2332013-02-27 19:06:30 +0000420bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
421 SkTypeface::Style style,
422 FontIdentity* outIdentity,
423 SkString* outFamilyName,
424 SkTypeface::Style* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700425 SkString familyStr(familyName ? familyName : "");
426 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000427 return false;
428 }
429
430 SkAutoMutexAcquire ac(mutex_);
431
reed@google.comb1c65b62013-02-26 15:50:51 +0000432 FcPattern* pattern = FcPatternCreate();
433
reed@google.comee619a02013-02-26 22:58:09 +0000434 if (familyName) {
435 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
436 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000437 FcPatternAddInteger(pattern, FC_WEIGHT,
438 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
439 : FC_WEIGHT_NORMAL);
440 FcPatternAddInteger(pattern, FC_SLANT,
441 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
442 : FC_SLANT_ROMAN);
443 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
444
halcanary96fcdcc2015-08-27 07:41:13 -0700445 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000446 FcDefaultSubstitute(pattern);
447
448 // Font matching:
449 // CSS often specifies a fallback list of families:
450 // font-family: a, b, c, serif;
451 // However, fontconfig will always do its best to find *a* font when asked
452 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700453 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000454 // and lets WebKit know to try the next CSS family name. However, fontconfig
455 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
456 // wish to support that.
457 //
458 // Thus, if a specific family is requested we set @family_requested. Then we
459 // record two strings: the family name after config processing and the
460 // family name after resolving. If the two are equal, it's a good match.
461 //
462 // So consider the case where a user has mapped Arial to Helvetica in their
463 // config.
464 // requested family: "Arial"
465 // post_config_family: "Helvetica"
466 // post_match_family: "Helvetica"
467 // -> good match
468 //
469 // and for a missing font:
470 // requested family: "Monaco"
471 // post_config_family: "Monaco"
472 // post_match_family: "Times New Roman"
473 // -> BAD match
474 //
475 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000476
477 const char* post_config_family = get_name(pattern, FC_FAMILY);
478 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000479 // we can just continue with an empty name, e.g. default font
480 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000481 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000482
483 FcResult result;
484 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
485 if (!font_set) {
486 FcPatternDestroy(pattern);
487 return false;
488 }
489
benjaminwagner2211a7b2015-12-01 11:12:05 -0800490 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000491 if (!match) {
492 FcPatternDestroy(pattern);
493 FcFontSetDestroy(font_set);
494 return false;
495 }
496
497 FcPatternDestroy(pattern);
498
reed@google.comf71a2332013-02-27 19:06:30 +0000499 // From here out we just extract our results from 'match'
500
reed@google.come49d67e2013-04-22 18:00:06 +0000501 post_config_family = get_name(match, FC_FAMILY);
502 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000503 FcFontSetDestroy(font_set);
504 return false;
505 }
506
reed@google.come49d67e2013-04-22 18:00:06 +0000507 const char* c_filename = get_name(match, FC_FILE);
508 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000509 FcFontSetDestroy(font_set);
510 return false;
511 }
reed@google.comf71a2332013-02-27 19:06:30 +0000512
reed@google.comb1c65b62013-02-26 15:50:51 +0000513 int face_index;
514 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
515 FcFontSetDestroy(font_set);
516 return false;
517 }
518
reed@google.comb1c65b62013-02-26 15:50:51 +0000519 FcFontSetDestroy(font_set);
520
reed@google.comf71a2332013-02-27 19:06:30 +0000521 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000522 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000523 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000524 }
reed@google.comf71a2332013-02-27 19:06:30 +0000525 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000526 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000527 }
reed@google.comf71a2332013-02-27 19:06:30 +0000528 if (outStyle) {
529 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000530 }
reed@google.comee619a02013-02-26 22:58:09 +0000531 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000532}
533
bungeman5f213d92015-01-27 05:39:10 -0800534SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000535 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000536}
reed@google.com027fd202013-04-19 20:45:30 +0000537
538///////////////////////////////////////////////////////////////////////////////
539
reed@google.com027fd202013-04-19 20:45:30 +0000540static bool find_name(const SkTDArray<const char*>& list, const char* str) {
541 int count = list.count();
542 for (int i = 0; i < count; ++i) {
543 if (!strcmp(list[i], str)) {
544 return true;
545 }
546 }
547 return false;
548}
549
550SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000551 SkAutoMutexAcquire ac(mutex_);
552
reed@google.com027fd202013-04-19 20:45:30 +0000553 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000554 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
halcanary96fcdcc2015-08-27 07:41:13 -0700555 if (nullptr == pat) {
556 return nullptr;
bungeman@google.com02dd6882013-07-22 14:10:08 +0000557 }
558
559 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
560 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
halcanary96fcdcc2015-08-27 07:41:13 -0700561 if (nullptr == os) {
562 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000563 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000564
halcanary96fcdcc2015-08-27 07:41:13 -0700565 FcFontSet* fs = FcFontList(nullptr, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000566 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
halcanary96fcdcc2015-08-27 07:41:13 -0700567 if (nullptr == fs) {
568 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000569 }
570
571 SkTDArray<const char*> names;
572 SkTDArray<size_t> sizes;
573 for (int i = 0; i < fs->nfont; ++i) {
574 FcPattern* match = fs->fonts[i];
575 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000576 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000577 *names.append() = famName;
578 *sizes.append() = strlen(famName) + 1;
579 }
580 }
581
reed@google.com027fd202013-04-19 20:45:30 +0000582 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
583 sizes.begin(), names.count());
584}
585
586bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
587 SkString* outFamilyName,
588 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000589 SkAutoMutexAcquire ac(mutex_);
590
591#if 0
mtkleinba59a672014-08-04 10:18:27 -0700592 SkString familyStr(familyName ? familyName : "");
593 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comf55061f2013-04-22 18:48:45 +0000594 return false;
595 }
596
597 SkAutoMutexAcquire ac(mutex_);
598
599 FcPattern* pattern = FcPatternCreate();
600
601 if (familyName) {
602 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
603 }
604 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
605
halcanary96fcdcc2015-08-27 07:41:13 -0700606 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comf55061f2013-04-22 18:48:45 +0000607 FcDefaultSubstitute(pattern);
608
609 // Font matching:
610 // CSS often specifies a fallback list of families:
611 // font-family: a, b, c, serif;
612 // However, fontconfig will always do its best to find *a* font when asked
613 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700614 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comf55061f2013-04-22 18:48:45 +0000615 // and lets WebKit know to try the next CSS family name. However, fontconfig
616 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
617 // wish to support that.
618 //
619 // Thus, if a specific family is requested we set @family_requested. Then we
620 // record two strings: the family name after config processing and the
621 // family name after resolving. If the two are equal, it's a good match.
622 //
623 // So consider the case where a user has mapped Arial to Helvetica in their
624 // config.
625 // requested family: "Arial"
626 // post_config_family: "Helvetica"
627 // post_match_family: "Helvetica"
628 // -> good match
629 //
630 // and for a missing font:
631 // requested family: "Monaco"
632 // post_config_family: "Monaco"
633 // post_match_family: "Times New Roman"
634 // -> BAD match
635 //
636 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
637
638 const char* post_config_family = get_name(pattern, FC_FAMILY);
639
640 FcResult result;
641 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
642 if (!font_set) {
643 FcPatternDestroy(pattern);
644 return false;
645 }
646
benjaminwagner2211a7b2015-12-01 11:12:05 -0800647 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comf55061f2013-04-22 18:48:45 +0000648 if (!match) {
649 FcPatternDestroy(pattern);
650 FcFontSetDestroy(font_set);
651 return false;
652 }
653
654 FcPatternDestroy(pattern);
655
656 // From here out we just extract our results from 'match'
657
658 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
659 FcFontSetDestroy(font_set);
660 return false;
661 }
662
663 FcChar8* c_filename;
664 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
665 FcFontSetDestroy(font_set);
666 return false;
667 }
668
669 int face_index;
670 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
671 FcFontSetDestroy(font_set);
672 return false;
673 }
674
675 FcFontSetDestroy(font_set);
676
677 if (outIdentity) {
678 outIdentity->fTTCIndex = face_index;
679 outIdentity->fString.set((const char*)c_filename);
680 }
681 if (outFamilyName) {
682 outFamilyName->set((const char*)post_config_family);
683 }
684 if (outStyle) {
685 *outStyle = GetFontStyle(match);
686 }
687 return true;
688
689////////////////////
690
691 int count;
benjaminwagner2211a7b2015-12-01 11:12:05 -0800692 FcPattern** match = this->MatchFont(font_set, post_config_family, &count);
reed@google.comf55061f2013-04-22 18:48:45 +0000693 if (!match) {
694 FcPatternDestroy(pattern);
695 FcFontSetDestroy(font_set);
halcanary96fcdcc2015-08-27 07:41:13 -0700696 return nullptr;
reed@google.comf55061f2013-04-22 18:48:45 +0000697 }
698
699 FcPatternDestroy(pattern);
700
701 SkTDArray<FcPattern*> trimmedMatches;
702 for (int i = 0; i < count; ++i) {
703 const char* justName = find_just_name(get_name(match[i], FC_FILE));
704 if (!is_lower(*justName)) {
705 *trimmedMatches.append() = match[i];
706 }
707 }
708
halcanary385fe4d2015-08-26 13:07:48 -0700709 SkFontStyleSet_FC* sset = new SkFontStyleSet_FC (trimmedMatches.begin(), trimmedMatches.count());
reed@google.comf55061f2013-04-22 18:48:45 +0000710#endif
reed@google.com027fd202013-04-19 20:45:30 +0000711 return false;
712}