blob: 0f2d882ca850919619e93de1168a283164afc971 [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"
reed@google.comb1c65b62013-02-26 15:50:51 +000018#include "SkStream.h"
mtkleinba59a672014-08-04 10:18:27 -070019#include "SkString.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000020
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;
bungeman5f213d92015-01-27 05:39:10 -0800115 SkStreamAsset* 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
mtklein72c9faa2015-01-09 10:06:39 -0800118 SkDataTable* getFamilyNames() SK_OVERRIDE;
reed@google.com027fd202013-04-19 20:45:30 +0000119 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
tomhudsone438ddb2014-07-01 18:54:41 -0700127SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface(SkBaseMutex* mutex) {
128 SkAutoMutexAcquire ac(mutex);
129 static SkFontConfigInterfaceDirect* singleton = NULL;
130 if (singleton == NULL) {
131 singleton = SkNEW(SkFontConfigInterfaceDirect);
132 }
133 return singleton;
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" },
jshin7476cf52014-09-09 12:30:57 -0700224 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000225 { PGOTHIC, "IPAPGothic" },
226 { PGOTHIC, "MotoyaG04Gothic" },
227
228 // MS ゴシック
229 { GOTHIC, "MS Gothic" },
230 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
231 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
232 { GOTHIC, "IPAGothic" },
233 { GOTHIC, "MotoyaG04GothicMono" },
234
235 // MS P明朝
236 { PMINCHO, "MS PMincho" },
237 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
238 "\xe6\x98\x8e\xe6\x9c\x9d"},
239 { PMINCHO, "IPAPMincho" },
240 { PMINCHO, "MotoyaG04Mincho" },
241
242 // MS 明朝
243 { MINCHO, "MS Mincho" },
244 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
245 { MINCHO, "IPAMincho" },
246 { MINCHO, "MotoyaG04MinchoMono" },
247
248 // 宋体
249 { SIMSUN, "Simsun" },
250 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
251 { SIMSUN, "MSung GB18030" },
252 { SIMSUN, "Song ASC" },
253
254 // 新宋体
255 { NSIMSUN, "NSimsun" },
256 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
257 { NSIMSUN, "MSung GB18030" },
258 { NSIMSUN, "N Song ASC" },
259
260 // 黑体
261 { SIMHEI, "Simhei" },
262 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700263 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000264 { SIMHEI, "MYingHeiGB18030" },
265 { SIMHEI, "MYingHeiB5HK" },
266
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000267 // 新細明體
268 { PMINGLIU, "PMingLiU"},
269 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
270 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000271
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000272 // 細明體
273 { MINGLIU, "MingLiU"},
274 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
275 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000276
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000277 // 新細明體
278 { PMINGLIUHK, "PMingLiU_HKSCS"},
279 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
280 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000281
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000282 // 細明體
283 { MINGLIUHK, "MingLiU_HKSCS"},
284 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
285 { MINGLIUHK, "MSung B5HK"},
286
287 // Cambria
288 { CAMBRIA, "Cambria" },
289 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000290
291 // Calibri
292 { CALIBRI, "Calibri" },
293 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000294 };
295
296 static const size_t kFontCount =
297 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
298
299 // TODO(jungshik): If this loop turns out to be hot, turn
300 // the array to a static (hash)map to speed it up.
301 for (size_t i = 0; i < kFontCount; ++i) {
302 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
303 return kFontEquivMap[i].clazz;
304 }
305 return OTHER;
306}
307
308
309// Return true if |font_a| and |font_b| are visually and at the metrics
310// level interchangeable.
311bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
312{
313 FontEquivClass class_a = GetFontEquivClass(font_a);
314 FontEquivClass class_b = GetFontEquivClass(font_b);
315
316 return class_a != OTHER && class_a == class_b;
317}
318
reed@google.comb1c65b62013-02-26 15:50:51 +0000319// Normally we only return exactly the font asked for. In last-resort
320// cases, the request either doesn't specify a font or is one of the
321// basic font names like "Sans", "Serif" or "Monospace". This function
322// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700323bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000324 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700325 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000326 strcasecmp(family_cstr, "sans") == 0 ||
327 strcasecmp(family_cstr, "serif") == 0 ||
328 strcasecmp(family_cstr, "monospace") == 0;
329}
330
reed@google.come49d67e2013-04-22 18:00:06 +0000331static bool valid_pattern(FcPattern* pattern) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000332#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
reed@google.come49d67e2013-04-22 18:00:06 +0000333 FcBool is_scalable;
334 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
335 || !is_scalable) {
336 return false;
337 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000338#endif
reed@google.come49d67e2013-04-22 18:00:06 +0000339
340 // fontconfig can also return fonts which are unreadable
341 const char* c_filename = get_name(pattern, FC_FILE);
342 if (!c_filename) {
343 return false;
344 }
345 if (access(c_filename, R_OK) != 0) {
346 return false;
347 }
348 return true;
349}
350
reed@google.comb1c65b62013-02-26 15:50:51 +0000351// Find matching font from |font_set| for the given font family.
352FcPattern* MatchFont(FcFontSet* font_set,
reed@google.come49d67e2013-04-22 18:00:06 +0000353 const char* post_config_family,
mtkleinba59a672014-08-04 10:18:27 -0700354 const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000355 // Older versions of fontconfig have a bug where they cannot select
356 // only scalable fonts so we have to manually filter the results.
357 FcPattern* match = NULL;
358 for (int i = 0; i < font_set->nfont; ++i) {
359 FcPattern* current = font_set->fonts[i];
reed@google.come49d67e2013-04-22 18:00:06 +0000360 if (valid_pattern(current)) {
361 match = current;
362 break;
reed@google.comb1c65b62013-02-26 15:50:51 +0000363 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000364 }
365
366 if (match && !IsFallbackFontAllowed(family)) {
367 bool acceptable_substitute = false;
368 for (int id = 0; id < 255; ++id) {
reed@google.come49d67e2013-04-22 18:00:06 +0000369 const char* post_match_family = get_name(match, FC_FAMILY, id);
370 if (!post_match_family)
reed@google.comb1c65b62013-02-26 15:50:51 +0000371 break;
372 acceptable_substitute =
reed@google.come49d67e2013-04-22 18:00:06 +0000373 (strcasecmp(post_config_family, post_match_family) == 0 ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000374 // Workaround for Issue 12530:
375 // requested family: "Bitstream Vera Sans"
376 // post_config_family: "Arial"
377 // post_match_family: "Bitstream Vera Sans"
378 // -> We should treat this case as a good match.
reed@google.come49d67e2013-04-22 18:00:06 +0000379 strcasecmp(family.c_str(), post_match_family) == 0) ||
380 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000381 if (acceptable_substitute)
382 break;
383 }
384 if (!acceptable_substitute)
385 return NULL;
386 }
387
388 return match;
389}
390
391// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000392SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000393 int resulting_bold;
394 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
395 resulting_bold = FC_WEIGHT_NORMAL;
396
397 int resulting_italic;
398 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
399 resulting_italic = FC_SLANT_ROMAN;
400
401 // If we ask for an italic font, fontconfig might take a roman font and set
402 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
403 // that the font is italic or oblique. So, if we see a matrix, we don't
404 // believe that it's italic.
405 FcValue matrix;
406 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
407
408 // If we ask for an italic font, fontconfig might take a roman font and set
409 // FC_EMBOLDEN.
410 FcValue embolden;
411 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
412
413 int styleBits = 0;
414 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
415 styleBits |= SkTypeface::kBold;
416 }
417 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
418 styleBits |= SkTypeface::kItalic;
419 }
420
reed@google.comf71a2332013-02-27 19:06:30 +0000421 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000422}
423
424} // anonymous namespace
425
426///////////////////////////////////////////////////////////////////////////////
427
reed@google.comf71a2332013-02-27 19:06:30 +0000428#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000429
reed@google.comf71a2332013-02-27 19:06:30 +0000430SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000431 SkAutoMutexAcquire ac(mutex_);
432
reed@google.comb1c65b62013-02-26 15:50:51 +0000433 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000434
435 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000436}
437
438SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
439}
440
reed@google.comf71a2332013-02-27 19:06:30 +0000441bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
442 SkTypeface::Style style,
443 FontIdentity* outIdentity,
444 SkString* outFamilyName,
445 SkTypeface::Style* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700446 SkString familyStr(familyName ? familyName : "");
447 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000448 return false;
449 }
450
451 SkAutoMutexAcquire ac(mutex_);
452
reed@google.comb1c65b62013-02-26 15:50:51 +0000453 FcPattern* pattern = FcPatternCreate();
454
reed@google.comee619a02013-02-26 22:58:09 +0000455 if (familyName) {
456 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
457 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000458 FcPatternAddInteger(pattern, FC_WEIGHT,
459 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
460 : FC_WEIGHT_NORMAL);
461 FcPatternAddInteger(pattern, FC_SLANT,
462 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
463 : FC_SLANT_ROMAN);
464 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
465
466 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
467 FcDefaultSubstitute(pattern);
468
469 // Font matching:
470 // CSS often specifies a fallback list of families:
471 // font-family: a, b, c, serif;
472 // However, fontconfig will always do its best to find *a* font when asked
473 // for something so we need a way to tell if the match which it has found is
474 // "good enough" for us. Otherwise, we can return NULL which gets piped up
475 // and lets WebKit know to try the next CSS family name. However, fontconfig
476 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
477 // wish to support that.
478 //
479 // Thus, if a specific family is requested we set @family_requested. Then we
480 // record two strings: the family name after config processing and the
481 // family name after resolving. If the two are equal, it's a good match.
482 //
483 // So consider the case where a user has mapped Arial to Helvetica in their
484 // config.
485 // requested family: "Arial"
486 // post_config_family: "Helvetica"
487 // post_match_family: "Helvetica"
488 // -> good match
489 //
490 // and for a missing font:
491 // requested family: "Monaco"
492 // post_config_family: "Monaco"
493 // post_match_family: "Times New Roman"
494 // -> BAD match
495 //
496 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000497
498 const char* post_config_family = get_name(pattern, FC_FAMILY);
499 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000500 // we can just continue with an empty name, e.g. default font
501 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000502 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000503
504 FcResult result;
505 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
506 if (!font_set) {
507 FcPatternDestroy(pattern);
508 return false;
509 }
510
reed@google.comee619a02013-02-26 22:58:09 +0000511 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000512 if (!match) {
513 FcPatternDestroy(pattern);
514 FcFontSetDestroy(font_set);
515 return false;
516 }
517
518 FcPatternDestroy(pattern);
519
reed@google.comf71a2332013-02-27 19:06:30 +0000520 // From here out we just extract our results from 'match'
521
reed@google.come49d67e2013-04-22 18:00:06 +0000522 post_config_family = get_name(match, FC_FAMILY);
523 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000524 FcFontSetDestroy(font_set);
525 return false;
526 }
527
reed@google.come49d67e2013-04-22 18:00:06 +0000528 const char* c_filename = get_name(match, FC_FILE);
529 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000530 FcFontSetDestroy(font_set);
531 return false;
532 }
reed@google.comf71a2332013-02-27 19:06:30 +0000533
reed@google.comb1c65b62013-02-26 15:50:51 +0000534 int face_index;
535 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
536 FcFontSetDestroy(font_set);
537 return false;
538 }
539
reed@google.comb1c65b62013-02-26 15:50:51 +0000540 FcFontSetDestroy(font_set);
541
reed@google.comf71a2332013-02-27 19:06:30 +0000542 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000543 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000544 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000545 }
reed@google.comf71a2332013-02-27 19:06:30 +0000546 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000547 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000548 }
reed@google.comf71a2332013-02-27 19:06:30 +0000549 if (outStyle) {
550 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000551 }
reed@google.comee619a02013-02-26 22:58:09 +0000552 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000553}
554
bungeman5f213d92015-01-27 05:39:10 -0800555SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000556 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000557}
reed@google.com027fd202013-04-19 20:45:30 +0000558
559///////////////////////////////////////////////////////////////////////////////
560
reed@google.com027fd202013-04-19 20:45:30 +0000561static bool find_name(const SkTDArray<const char*>& list, const char* str) {
562 int count = list.count();
563 for (int i = 0; i < count; ++i) {
564 if (!strcmp(list[i], str)) {
565 return true;
566 }
567 }
568 return false;
569}
570
571SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000572 SkAutoMutexAcquire ac(mutex_);
573
reed@google.com027fd202013-04-19 20:45:30 +0000574 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000575 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
576 if (NULL == pat) {
577 return NULL;
578 }
579
580 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
581 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
reed@google.com027fd202013-04-19 20:45:30 +0000582 if (NULL == os) {
reed@google.comdf798b32013-04-19 20:52:50 +0000583 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000584 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000585
reed@google.com027fd202013-04-19 20:45:30 +0000586 FcFontSet* fs = FcFontList(NULL, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000587 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
reed@google.com027fd202013-04-19 20:45:30 +0000588 if (NULL == fs) {
reed@google.comdf798b32013-04-19 20:52:50 +0000589 return NULL;
reed@google.com027fd202013-04-19 20:45:30 +0000590 }
591
592 SkTDArray<const char*> names;
593 SkTDArray<size_t> sizes;
594 for (int i = 0; i < fs->nfont; ++i) {
595 FcPattern* match = fs->fonts[i];
596 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000597 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000598 *names.append() = famName;
599 *sizes.append() = strlen(famName) + 1;
600 }
601 }
602
reed@google.com027fd202013-04-19 20:45:30 +0000603 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
604 sizes.begin(), names.count());
605}
606
607bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
608 SkString* outFamilyName,
609 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000610 SkAutoMutexAcquire ac(mutex_);
611
612#if 0
mtkleinba59a672014-08-04 10:18:27 -0700613 SkString familyStr(familyName ? familyName : "");
614 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comf55061f2013-04-22 18:48:45 +0000615 return false;
616 }
617
618 SkAutoMutexAcquire ac(mutex_);
619
620 FcPattern* pattern = FcPatternCreate();
621
622 if (familyName) {
623 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
624 }
625 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
626
627 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
628 FcDefaultSubstitute(pattern);
629
630 // Font matching:
631 // CSS often specifies a fallback list of families:
632 // font-family: a, b, c, serif;
633 // However, fontconfig will always do its best to find *a* font when asked
634 // for something so we need a way to tell if the match which it has found is
635 // "good enough" for us. Otherwise, we can return NULL which gets piped up
636 // and lets WebKit know to try the next CSS family name. However, fontconfig
637 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
638 // wish to support that.
639 //
640 // Thus, if a specific family is requested we set @family_requested. Then we
641 // record two strings: the family name after config processing and the
642 // family name after resolving. If the two are equal, it's a good match.
643 //
644 // So consider the case where a user has mapped Arial to Helvetica in their
645 // config.
646 // requested family: "Arial"
647 // post_config_family: "Helvetica"
648 // post_match_family: "Helvetica"
649 // -> good match
650 //
651 // and for a missing font:
652 // requested family: "Monaco"
653 // post_config_family: "Monaco"
654 // post_match_family: "Times New Roman"
655 // -> BAD match
656 //
657 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
658
659 const char* post_config_family = get_name(pattern, FC_FAMILY);
660
661 FcResult result;
662 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
663 if (!font_set) {
664 FcPatternDestroy(pattern);
665 return false;
666 }
667
668 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
669 if (!match) {
670 FcPatternDestroy(pattern);
671 FcFontSetDestroy(font_set);
672 return false;
673 }
674
675 FcPatternDestroy(pattern);
676
677 // From here out we just extract our results from 'match'
678
679 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
680 FcFontSetDestroy(font_set);
681 return false;
682 }
683
684 FcChar8* c_filename;
685 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
686 FcFontSetDestroy(font_set);
687 return false;
688 }
689
690 int face_index;
691 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
692 FcFontSetDestroy(font_set);
693 return false;
694 }
695
696 FcFontSetDestroy(font_set);
697
698 if (outIdentity) {
699 outIdentity->fTTCIndex = face_index;
700 outIdentity->fString.set((const char*)c_filename);
701 }
702 if (outFamilyName) {
703 outFamilyName->set((const char*)post_config_family);
704 }
705 if (outStyle) {
706 *outStyle = GetFontStyle(match);
707 }
708 return true;
709
710////////////////////
711
712 int count;
713 FcPattern** match = MatchFont(font_set, post_config_family, &count);
714 if (!match) {
715 FcPatternDestroy(pattern);
716 FcFontSetDestroy(font_set);
717 return NULL;
718 }
719
720 FcPatternDestroy(pattern);
721
722 SkTDArray<FcPattern*> trimmedMatches;
723 for (int i = 0; i < count; ++i) {
724 const char* justName = find_just_name(get_name(match[i], FC_FILE));
725 if (!is_lower(*justName)) {
726 *trimmedMatches.append() = match[i];
727 }
728 }
729
730 SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
731 (trimmedMatches.begin(),
732 trimmedMatches.count()));
733#endif
reed@google.com027fd202013-04-19 20:45:30 +0000734 return false;
735}