blob: 8a8e4a1b538dfcfa270ed40b24d9f60adb3be023 [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.comf55061f2013-04-22 18:48:45 +000010#include "SkBuffer.h"
bungemanf20488b2015-07-29 11:49:40 -070011#include "SkDataTable.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000012#include "SkFontConfigInterface.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.comb1c65b62013-02-26 15:50:51 +0000110class SkFontConfigInterfaceDirect : public SkFontConfigInterface {
111public:
112 SkFontConfigInterfaceDirect();
113 virtual ~SkFontConfigInterfaceDirect();
114
reed@google.comf71a2332013-02-27 19:06:30 +0000115 virtual bool matchFamilyName(const char familyName[],
116 SkTypeface::Style requested,
117 FontIdentity* outFontIdentifier,
118 SkString* outFamilyName,
mtklein36352bf2015-03-25 18:17:31 -0700119 SkTypeface::Style* outStyle) override;
120 SkStreamAsset* openStream(const FontIdentity&) override;
reed@google.comb1c65b62013-02-26 15:50:51 +0000121
reed@google.com027fd202013-04-19 20:45:30 +0000122 // new APIs
mtklein36352bf2015-03-25 18:17:31 -0700123 SkDataTable* getFamilyNames() override;
reed@google.com027fd202013-04-19 20:45:30 +0000124 virtual bool matchFamilySet(const char inFamilyName[],
125 SkString* outFamilyName,
mtklein36352bf2015-03-25 18:17:31 -0700126 SkTArray<FontIdentity>*) override;
reed@google.com027fd202013-04-19 20:45:30 +0000127
reed@google.comb1c65b62013-02-26 15:50:51 +0000128private:
129 SkMutex mutex_;
reed@google.comb1c65b62013-02-26 15:50:51 +0000130};
131
tomhudsone438ddb2014-07-01 18:54:41 -0700132SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface(SkBaseMutex* mutex) {
133 SkAutoMutexAcquire ac(mutex);
halcanary96fcdcc2015-08-27 07:41:13 -0700134 static SkFontConfigInterfaceDirect* singleton = nullptr;
135 if (singleton == nullptr) {
halcanary385fe4d2015-08-26 13:07:48 -0700136 singleton = new SkFontConfigInterfaceDirect;
tomhudsone438ddb2014-07-01 18:54:41 -0700137 }
138 return singleton;
reed@google.comb1c65b62013-02-26 15:50:51 +0000139}
140
reed@google.com54c69142013-04-09 15:54:52 +0000141///////////////////////////////////////////////////////////////////////////////
142
halcanary96fcdcc2015-08-27 07:41:13 -0700143// Returns the string from the pattern, or nullptr
reed@google.come49d67e2013-04-22 18:00:06 +0000144static const char* get_name(FcPattern* pattern, const char field[],
145 int index = 0) {
146 const char* name;
147 if (FcPatternGetString(pattern, field, index,
148 (FcChar8**)&name) != FcResultMatch) {
halcanary96fcdcc2015-08-27 07:41:13 -0700149 name = nullptr;
reed@google.come49d67e2013-04-22 18:00:06 +0000150 }
151 return name;
152}
153
154///////////////////////////////////////////////////////////////////////////////
155
reed@google.comb1c65b62013-02-26 15:50:51 +0000156namespace {
157
158// Equivalence classes, used to match the Liberation and other fonts
159// with their metric-compatible replacements. See the discussion in
160// GetFontEquivClass().
161enum FontEquivClass
162{
163 OTHER,
164 SANS,
165 SERIF,
166 MONO,
167 SYMBOL,
168 PGOTHIC,
169 GOTHIC,
170 PMINCHO,
171 MINCHO,
172 SIMSUN,
173 NSIMSUN,
174 SIMHEI,
175 PMINGLIU,
176 MINGLIU,
177 PMINGLIUHK,
178 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000179 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000180 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000181};
182
183// Match the font name against a whilelist of fonts, returning the equivalence
184// class.
185FontEquivClass GetFontEquivClass(const char* fontname)
186{
187 // It would be nice for fontconfig to tell us whether a given suggested
188 // replacement is a "strong" match (that is, an equivalent font) or
189 // a "weak" match (that is, fontconfig's next-best attempt at finding a
190 // substitute). However, I played around with the fontconfig API for
191 // a good few hours and could not make it reveal this information.
192 //
193 // So instead, we hardcode. Initially this function emulated
194 // /etc/fonts/conf.d/30-metric-aliases.conf
195 // from my Ubuntu system, but we're better off being very conservative.
196
197 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
198 // Arial, Times New Roman and Courier New with a character repertoire
199 // much larger than Liberation. Note that Cousine is metrically
200 // compatible with Courier New, but the former is sans-serif while
201 // the latter is serif.
202
203
204 struct FontEquivMap {
205 FontEquivClass clazz;
206 const char name[40];
207 };
208
209 static const FontEquivMap kFontEquivMap[] = {
210 { SANS, "Arial" },
211 { SANS, "Arimo" },
212 { SANS, "Liberation Sans" },
213
214 { SERIF, "Times New Roman" },
215 { SERIF, "Tinos" },
216 { SERIF, "Liberation Serif" },
217
218 { MONO, "Courier New" },
219 { MONO, "Cousine" },
220 { MONO, "Liberation Mono" },
221
222 { SYMBOL, "Symbol" },
223 { SYMBOL, "Symbol Neu" },
224
225 // MS Pゴシック
226 { PGOTHIC, "MS PGothic" },
227 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
228 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshin7476cf52014-09-09 12:30:57 -0700229 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000230 { PGOTHIC, "IPAPGothic" },
231 { PGOTHIC, "MotoyaG04Gothic" },
232
233 // MS ゴシック
234 { GOTHIC, "MS Gothic" },
235 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
236 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshindd4e5682015-05-12 12:08:36 -0700237 { GOTHIC, "Noto Sans Mono CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000238 { GOTHIC, "IPAGothic" },
239 { GOTHIC, "MotoyaG04GothicMono" },
240
241 // MS P明朝
242 { PMINCHO, "MS PMincho" },
243 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
244 "\xe6\x98\x8e\xe6\x9c\x9d"},
245 { PMINCHO, "IPAPMincho" },
246 { PMINCHO, "MotoyaG04Mincho" },
247
248 // MS 明朝
249 { MINCHO, "MS Mincho" },
250 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
251 { MINCHO, "IPAMincho" },
252 { MINCHO, "MotoyaG04MinchoMono" },
253
254 // 宋体
255 { SIMSUN, "Simsun" },
256 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
257 { SIMSUN, "MSung GB18030" },
258 { SIMSUN, "Song ASC" },
259
260 // 新宋体
261 { NSIMSUN, "NSimsun" },
262 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
263 { NSIMSUN, "MSung GB18030" },
264 { NSIMSUN, "N Song ASC" },
265
266 // 黑体
267 { SIMHEI, "Simhei" },
268 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700269 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000270 { SIMHEI, "MYingHeiGB18030" },
271 { SIMHEI, "MYingHeiB5HK" },
272
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000273 // 新細明體
274 { PMINGLIU, "PMingLiU"},
275 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
276 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000277
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000278 // 細明體
279 { MINGLIU, "MingLiU"},
280 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
281 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000282
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000283 // 新細明體
284 { PMINGLIUHK, "PMingLiU_HKSCS"},
285 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
286 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000287
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000288 // 細明體
289 { MINGLIUHK, "MingLiU_HKSCS"},
290 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
291 { MINGLIUHK, "MSung B5HK"},
292
293 // Cambria
294 { CAMBRIA, "Cambria" },
295 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000296
297 // Calibri
298 { CALIBRI, "Calibri" },
299 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000300 };
301
302 static const size_t kFontCount =
303 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
304
305 // TODO(jungshik): If this loop turns out to be hot, turn
306 // the array to a static (hash)map to speed it up.
307 for (size_t i = 0; i < kFontCount; ++i) {
308 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
309 return kFontEquivMap[i].clazz;
310 }
311 return OTHER;
312}
313
314
315// Return true if |font_a| and |font_b| are visually and at the metrics
316// level interchangeable.
317bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
318{
319 FontEquivClass class_a = GetFontEquivClass(font_a);
320 FontEquivClass class_b = GetFontEquivClass(font_b);
321
322 return class_a != OTHER && class_a == class_b;
323}
324
reed@google.comb1c65b62013-02-26 15:50:51 +0000325// Normally we only return exactly the font asked for. In last-resort
326// cases, the request either doesn't specify a font or is one of the
327// basic font names like "Sans", "Serif" or "Monospace". This function
328// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700329bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000330 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700331 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000332 strcasecmp(family_cstr, "sans") == 0 ||
333 strcasecmp(family_cstr, "serif") == 0 ||
334 strcasecmp(family_cstr, "monospace") == 0;
335}
336
reed@google.come49d67e2013-04-22 18:00:06 +0000337static bool valid_pattern(FcPattern* pattern) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000338#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
reed@google.come49d67e2013-04-22 18:00:06 +0000339 FcBool is_scalable;
340 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
341 || !is_scalable) {
342 return false;
343 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000344#endif
reed@google.come49d67e2013-04-22 18:00:06 +0000345
346 // fontconfig can also return fonts which are unreadable
347 const char* c_filename = get_name(pattern, FC_FILE);
348 if (!c_filename) {
349 return false;
350 }
351 if (access(c_filename, R_OK) != 0) {
352 return false;
353 }
354 return true;
355}
356
reed@google.comb1c65b62013-02-26 15:50:51 +0000357// Find matching font from |font_set| for the given font family.
358FcPattern* MatchFont(FcFontSet* font_set,
reed@google.come49d67e2013-04-22 18:00:06 +0000359 const char* post_config_family,
mtkleinba59a672014-08-04 10:18:27 -0700360 const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000361 // Older versions of fontconfig have a bug where they cannot select
362 // only scalable fonts so we have to manually filter the results.
halcanary96fcdcc2015-08-27 07:41:13 -0700363 FcPattern* match = nullptr;
reed@google.comb1c65b62013-02-26 15:50:51 +0000364 for (int i = 0; i < font_set->nfont; ++i) {
365 FcPattern* current = font_set->fonts[i];
reed@google.come49d67e2013-04-22 18:00:06 +0000366 if (valid_pattern(current)) {
367 match = current;
368 break;
reed@google.comb1c65b62013-02-26 15:50:51 +0000369 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000370 }
371
372 if (match && !IsFallbackFontAllowed(family)) {
373 bool acceptable_substitute = false;
374 for (int id = 0; id < 255; ++id) {
reed@google.come49d67e2013-04-22 18:00:06 +0000375 const char* post_match_family = get_name(match, FC_FAMILY, id);
376 if (!post_match_family)
reed@google.comb1c65b62013-02-26 15:50:51 +0000377 break;
378 acceptable_substitute =
reed@google.come49d67e2013-04-22 18:00:06 +0000379 (strcasecmp(post_config_family, post_match_family) == 0 ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000380 // Workaround for Issue 12530:
381 // requested family: "Bitstream Vera Sans"
382 // post_config_family: "Arial"
383 // post_match_family: "Bitstream Vera Sans"
384 // -> We should treat this case as a good match.
reed@google.come49d67e2013-04-22 18:00:06 +0000385 strcasecmp(family.c_str(), post_match_family) == 0) ||
386 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000387 if (acceptable_substitute)
388 break;
389 }
390 if (!acceptable_substitute)
halcanary96fcdcc2015-08-27 07:41:13 -0700391 return nullptr;
reed@google.comb1c65b62013-02-26 15:50:51 +0000392 }
393
394 return match;
395}
396
397// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
reed@google.comf71a2332013-02-27 19:06:30 +0000398SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000399 int resulting_bold;
400 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
401 resulting_bold = FC_WEIGHT_NORMAL;
402
403 int resulting_italic;
404 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
405 resulting_italic = FC_SLANT_ROMAN;
406
407 // If we ask for an italic font, fontconfig might take a roman font and set
408 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
409 // that the font is italic or oblique. So, if we see a matrix, we don't
410 // believe that it's italic.
411 FcValue matrix;
412 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
413
414 // If we ask for an italic font, fontconfig might take a roman font and set
415 // FC_EMBOLDEN.
416 FcValue embolden;
417 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
418
419 int styleBits = 0;
420 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
421 styleBits |= SkTypeface::kBold;
422 }
423 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
424 styleBits |= SkTypeface::kItalic;
425 }
426
reed@google.comf71a2332013-02-27 19:06:30 +0000427 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000428}
429
430} // anonymous namespace
431
432///////////////////////////////////////////////////////////////////////////////
433
reed@google.comf71a2332013-02-27 19:06:30 +0000434#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000435
reed@google.comf71a2332013-02-27 19:06:30 +0000436SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000437 SkAutoMutexAcquire ac(mutex_);
438
reed@google.comb1c65b62013-02-26 15:50:51 +0000439 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000440
441 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000442}
443
444SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
445}
446
reed@google.comf71a2332013-02-27 19:06:30 +0000447bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
448 SkTypeface::Style style,
449 FontIdentity* outIdentity,
450 SkString* outFamilyName,
451 SkTypeface::Style* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700452 SkString familyStr(familyName ? familyName : "");
453 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000454 return false;
455 }
456
457 SkAutoMutexAcquire ac(mutex_);
458
reed@google.comb1c65b62013-02-26 15:50:51 +0000459 FcPattern* pattern = FcPatternCreate();
460
reed@google.comee619a02013-02-26 22:58:09 +0000461 if (familyName) {
462 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
463 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000464 FcPatternAddInteger(pattern, FC_WEIGHT,
465 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
466 : FC_WEIGHT_NORMAL);
467 FcPatternAddInteger(pattern, FC_SLANT,
468 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
469 : FC_SLANT_ROMAN);
470 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
471
halcanary96fcdcc2015-08-27 07:41:13 -0700472 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000473 FcDefaultSubstitute(pattern);
474
475 // Font matching:
476 // CSS often specifies a fallback list of families:
477 // font-family: a, b, c, serif;
478 // However, fontconfig will always do its best to find *a* font when asked
479 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700480 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000481 // and lets WebKit know to try the next CSS family name. However, fontconfig
482 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
483 // wish to support that.
484 //
485 // Thus, if a specific family is requested we set @family_requested. Then we
486 // record two strings: the family name after config processing and the
487 // family name after resolving. If the two are equal, it's a good match.
488 //
489 // So consider the case where a user has mapped Arial to Helvetica in their
490 // config.
491 // requested family: "Arial"
492 // post_config_family: "Helvetica"
493 // post_match_family: "Helvetica"
494 // -> good match
495 //
496 // and for a missing font:
497 // requested family: "Monaco"
498 // post_config_family: "Monaco"
499 // post_match_family: "Times New Roman"
500 // -> BAD match
501 //
502 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000503
504 const char* post_config_family = get_name(pattern, FC_FAMILY);
505 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000506 // we can just continue with an empty name, e.g. default font
507 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000508 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000509
510 FcResult result;
511 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
512 if (!font_set) {
513 FcPatternDestroy(pattern);
514 return false;
515 }
516
reed@google.comee619a02013-02-26 22:58:09 +0000517 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000518 if (!match) {
519 FcPatternDestroy(pattern);
520 FcFontSetDestroy(font_set);
521 return false;
522 }
523
524 FcPatternDestroy(pattern);
525
reed@google.comf71a2332013-02-27 19:06:30 +0000526 // From here out we just extract our results from 'match'
527
reed@google.come49d67e2013-04-22 18:00:06 +0000528 post_config_family = get_name(match, FC_FAMILY);
529 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000530 FcFontSetDestroy(font_set);
531 return false;
532 }
533
reed@google.come49d67e2013-04-22 18:00:06 +0000534 const char* c_filename = get_name(match, FC_FILE);
535 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000536 FcFontSetDestroy(font_set);
537 return false;
538 }
reed@google.comf71a2332013-02-27 19:06:30 +0000539
reed@google.comb1c65b62013-02-26 15:50:51 +0000540 int face_index;
541 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
542 FcFontSetDestroy(font_set);
543 return false;
544 }
545
reed@google.comb1c65b62013-02-26 15:50:51 +0000546 FcFontSetDestroy(font_set);
547
reed@google.comf71a2332013-02-27 19:06:30 +0000548 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000549 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000550 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000551 }
reed@google.comf71a2332013-02-27 19:06:30 +0000552 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000553 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000554 }
reed@google.comf71a2332013-02-27 19:06:30 +0000555 if (outStyle) {
556 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000557 }
reed@google.comee619a02013-02-26 22:58:09 +0000558 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000559}
560
bungeman5f213d92015-01-27 05:39:10 -0800561SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000562 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000563}
reed@google.com027fd202013-04-19 20:45:30 +0000564
565///////////////////////////////////////////////////////////////////////////////
566
reed@google.com027fd202013-04-19 20:45:30 +0000567static bool find_name(const SkTDArray<const char*>& list, const char* str) {
568 int count = list.count();
569 for (int i = 0; i < count; ++i) {
570 if (!strcmp(list[i], str)) {
571 return true;
572 }
573 }
574 return false;
575}
576
577SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000578 SkAutoMutexAcquire ac(mutex_);
579
reed@google.com027fd202013-04-19 20:45:30 +0000580 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000581 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
halcanary96fcdcc2015-08-27 07:41:13 -0700582 if (nullptr == pat) {
583 return nullptr;
bungeman@google.com02dd6882013-07-22 14:10:08 +0000584 }
585
586 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
587 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
halcanary96fcdcc2015-08-27 07:41:13 -0700588 if (nullptr == os) {
589 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000590 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000591
halcanary96fcdcc2015-08-27 07:41:13 -0700592 FcFontSet* fs = FcFontList(nullptr, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000593 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
halcanary96fcdcc2015-08-27 07:41:13 -0700594 if (nullptr == fs) {
595 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000596 }
597
598 SkTDArray<const char*> names;
599 SkTDArray<size_t> sizes;
600 for (int i = 0; i < fs->nfont; ++i) {
601 FcPattern* match = fs->fonts[i];
602 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000603 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000604 *names.append() = famName;
605 *sizes.append() = strlen(famName) + 1;
606 }
607 }
608
reed@google.com027fd202013-04-19 20:45:30 +0000609 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
610 sizes.begin(), names.count());
611}
612
613bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
614 SkString* outFamilyName,
615 SkTArray<FontIdentity>* ids) {
reed@google.comf55061f2013-04-22 18:48:45 +0000616 SkAutoMutexAcquire ac(mutex_);
617
618#if 0
mtkleinba59a672014-08-04 10:18:27 -0700619 SkString familyStr(familyName ? familyName : "");
620 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comf55061f2013-04-22 18:48:45 +0000621 return false;
622 }
623
624 SkAutoMutexAcquire ac(mutex_);
625
626 FcPattern* pattern = FcPatternCreate();
627
628 if (familyName) {
629 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
630 }
631 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
632
halcanary96fcdcc2015-08-27 07:41:13 -0700633 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comf55061f2013-04-22 18:48:45 +0000634 FcDefaultSubstitute(pattern);
635
636 // Font matching:
637 // CSS often specifies a fallback list of families:
638 // font-family: a, b, c, serif;
639 // However, fontconfig will always do its best to find *a* font when asked
640 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700641 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comf55061f2013-04-22 18:48:45 +0000642 // and lets WebKit know to try the next CSS family name. However, fontconfig
643 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
644 // wish to support that.
645 //
646 // Thus, if a specific family is requested we set @family_requested. Then we
647 // record two strings: the family name after config processing and the
648 // family name after resolving. If the two are equal, it's a good match.
649 //
650 // So consider the case where a user has mapped Arial to Helvetica in their
651 // config.
652 // requested family: "Arial"
653 // post_config_family: "Helvetica"
654 // post_match_family: "Helvetica"
655 // -> good match
656 //
657 // and for a missing font:
658 // requested family: "Monaco"
659 // post_config_family: "Monaco"
660 // post_match_family: "Times New Roman"
661 // -> BAD match
662 //
663 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
664
665 const char* post_config_family = get_name(pattern, FC_FAMILY);
666
667 FcResult result;
668 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
669 if (!font_set) {
670 FcPatternDestroy(pattern);
671 return false;
672 }
673
674 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
675 if (!match) {
676 FcPatternDestroy(pattern);
677 FcFontSetDestroy(font_set);
678 return false;
679 }
680
681 FcPatternDestroy(pattern);
682
683 // From here out we just extract our results from 'match'
684
685 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
686 FcFontSetDestroy(font_set);
687 return false;
688 }
689
690 FcChar8* c_filename;
691 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
692 FcFontSetDestroy(font_set);
693 return false;
694 }
695
696 int face_index;
697 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
698 FcFontSetDestroy(font_set);
699 return false;
700 }
701
702 FcFontSetDestroy(font_set);
703
704 if (outIdentity) {
705 outIdentity->fTTCIndex = face_index;
706 outIdentity->fString.set((const char*)c_filename);
707 }
708 if (outFamilyName) {
709 outFamilyName->set((const char*)post_config_family);
710 }
711 if (outStyle) {
712 *outStyle = GetFontStyle(match);
713 }
714 return true;
715
716////////////////////
717
718 int count;
719 FcPattern** match = MatchFont(font_set, post_config_family, &count);
720 if (!match) {
721 FcPatternDestroy(pattern);
722 FcFontSetDestroy(font_set);
halcanary96fcdcc2015-08-27 07:41:13 -0700723 return nullptr;
reed@google.comf55061f2013-04-22 18:48:45 +0000724 }
725
726 FcPatternDestroy(pattern);
727
728 SkTDArray<FcPattern*> trimmedMatches;
729 for (int i = 0; i < count; ++i) {
730 const char* justName = find_just_name(get_name(match[i], FC_FILE));
731 if (!is_lower(*justName)) {
732 *trimmedMatches.append() = match[i];
733 }
734 }
735
halcanary385fe4d2015-08-26 13:07:48 -0700736 SkFontStyleSet_FC* sset = new SkFontStyleSet_FC (trimmedMatches.begin(), trimmedMatches.count());
reed@google.comf55061f2013-04-22 18:48:45 +0000737#endif
reed@google.com027fd202013-04-19 20:45:30 +0000738 return false;
739}