blob: d6fa96a46cfa5c418516a860edbfc9af9937a385 [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"
bungeman11a77c62016-04-12 13:45:06 -070012#include "SkFixed.h"
benjaminwagner2211a7b2015-12-01 11:12:05 -080013#include "SkFontConfigInterface_direct.h"
bungemanf20488b2015-07-29 11:49:40 -070014#include "SkFontStyle.h"
mtklein1b249332015-07-07 12:21:21 -070015#include "SkMutex.h"
reed@google.comb1c65b62013-02-26 15:50:51 +000016#include "SkStream.h"
mtkleinba59a672014-08-04 10:18:27 -070017#include "SkString.h"
bungemanf20488b2015-07-29 11:49:40 -070018#include "SkTArray.h"
19#include "SkTDArray.h"
20#include "SkTemplates.h"
21#include "SkTypeface.h"
22#include "SkTypes.h"
23
24#include <fontconfig/fontconfig.h>
25#include <unistd.h>
reed@google.comb1c65b62013-02-26 15:50:51 +000026
bungeman02657072016-05-02 11:54:13 -070027#ifdef SK_DEBUG
28# include "SkTLS.h"
29#endif
30
31namespace {
32
33// Fontconfig is not threadsafe before 2.10.91. Before that, we lock with a global mutex.
34// See https://bug.skia.org/1497 for background.
35SK_DECLARE_STATIC_MUTEX(gFCMutex);
36
37#ifdef SK_DEBUG
38void* CreateThreadFcLocked() { return new bool(false); }
39void DeleteThreadFcLocked(void* v) { delete static_cast<bool*>(v); }
40# define THREAD_FC_LOCKED \
41 static_cast<bool*>(SkTLS::Get(CreateThreadFcLocked, DeleteThreadFcLocked))
42#endif
43
44struct FCLocker {
45 // Assume FcGetVersion() has always been thread safe.
46
47 FCLocker() {
48 if (FcGetVersion() < 21091) {
49 gFCMutex.acquire();
50 } else {
51 SkDEBUGCODE(bool* threadLocked = THREAD_FC_LOCKED);
52 SkASSERT(false == *threadLocked);
53 SkDEBUGCODE(*threadLocked = true);
54 }
55 }
56
57 ~FCLocker() {
58 AssertHeld();
59 if (FcGetVersion() < 21091) {
60 gFCMutex.release();
61 } else {
62 SkDEBUGCODE(*THREAD_FC_LOCKED = false);
63 }
64 }
65
66 static void AssertHeld() { SkDEBUGCODE(
67 if (FcGetVersion() < 21091) {
68 gFCMutex.assertHeld();
69 } else {
70 SkASSERT(true == *THREAD_FC_LOCKED);
71 }
72 ) }
73};
74
75} // namespace
76
reed@google.comf55061f2013-04-22 18:48:45 +000077size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
78 size_t size = sizeof(fID) + sizeof(fTTCIndex);
79 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
80 size += sizeof(int32_t) + fString.size(); // store length+data
81 if (addr) {
82 SkWBuffer buffer(addr, size);
83
84 buffer.write32(fID);
85 buffer.write32(fTTCIndex);
86 buffer.write32(fString.size());
87 buffer.write32(fStyle.weight());
88 buffer.write32(fStyle.width());
89 buffer.write8(fStyle.slant());
90 buffer.write(fString.c_str(), fString.size());
91 buffer.padToAlign4();
92
93 SkASSERT(buffer.pos() == size);
94 }
95 return size;
96}
97
98size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
99 size_t size) {
100 SkRBuffer buffer(addr, size);
101
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +0000102 (void)buffer.readU32(&fID);
103 (void)buffer.readS32(&fTTCIndex);
104 uint32_t strLen, weight, width;
105 (void)buffer.readU32(&strLen);
106 (void)buffer.readU32(&weight);
107 (void)buffer.readU32(&width);
108 uint8_t u8;
109 (void)buffer.readU8(&u8);
110 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
reed@google.comf55061f2013-04-22 18:48:45 +0000111 fStyle = SkFontStyle(weight, width, slant);
112 fString.resize(strLen);
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +0000113 (void)buffer.read(fString.writable_str(), strLen);
reed@google.comf55061f2013-04-22 18:48:45 +0000114 buffer.skipToAlign4();
115
116 return buffer.pos(); // the actual number of bytes read
117}
118
119#ifdef SK_DEBUG
120static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
121 iden->fID = 10;
122 iden->fTTCIndex = 2;
123 iden->fString.set("Hello world");
124 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
125}
126
127static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
128 int initValue) {
129 SkFontConfigInterface::FontIdentity iden1;
130
halcanary96fcdcc2015-08-27 07:41:13 -0700131 size_t size0 = iden0.writeToMemory(nullptr);
reed@google.comf55061f2013-04-22 18:48:45 +0000132
133 SkAutoMalloc storage(size0);
134 memset(storage.get(), initValue, size0);
135
136 size_t size1 = iden0.writeToMemory(storage.get());
137 SkASSERT(size0 == size1);
138
139 SkASSERT(iden0 != iden1);
140 size_t size2 = iden1.readFromMemory(storage.get(), size1);
141 SkASSERT(size2 == size1);
142 SkASSERT(iden0 == iden1);
143}
144
145static void fontconfiginterface_unittest() {
146 SkFontConfigInterface::FontIdentity iden0, iden1;
147
148 SkASSERT(iden0 == iden1);
149
150 make_iden(&iden0);
151 SkASSERT(iden0 != iden1);
152
153 make_iden(&iden1);
154 SkASSERT(iden0 == iden1);
155
156 test_writeToMemory(iden0, 0);
157 test_writeToMemory(iden0, 0);
158}
159#endif
160
reed@google.com54c69142013-04-09 15:54:52 +0000161///////////////////////////////////////////////////////////////////////////////
162
halcanary96fcdcc2015-08-27 07:41:13 -0700163// Returns the string from the pattern, or nullptr
reed@google.come49d67e2013-04-22 18:00:06 +0000164static const char* get_name(FcPattern* pattern, const char field[],
165 int index = 0) {
166 const char* name;
167 if (FcPatternGetString(pattern, field, index,
168 (FcChar8**)&name) != FcResultMatch) {
halcanary96fcdcc2015-08-27 07:41:13 -0700169 name = nullptr;
reed@google.come49d67e2013-04-22 18:00:06 +0000170 }
171 return name;
172}
173
174///////////////////////////////////////////////////////////////////////////////
175
reed@google.comb1c65b62013-02-26 15:50:51 +0000176namespace {
177
178// Equivalence classes, used to match the Liberation and other fonts
179// with their metric-compatible replacements. See the discussion in
180// GetFontEquivClass().
181enum FontEquivClass
182{
183 OTHER,
184 SANS,
185 SERIF,
186 MONO,
187 SYMBOL,
188 PGOTHIC,
189 GOTHIC,
190 PMINCHO,
191 MINCHO,
192 SIMSUN,
193 NSIMSUN,
194 SIMHEI,
195 PMINGLIU,
196 MINGLIU,
197 PMINGLIUHK,
198 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000199 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000200 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000201};
202
203// Match the font name against a whilelist of fonts, returning the equivalence
204// class.
205FontEquivClass GetFontEquivClass(const char* fontname)
206{
207 // It would be nice for fontconfig to tell us whether a given suggested
208 // replacement is a "strong" match (that is, an equivalent font) or
209 // a "weak" match (that is, fontconfig's next-best attempt at finding a
210 // substitute). However, I played around with the fontconfig API for
211 // a good few hours and could not make it reveal this information.
212 //
213 // So instead, we hardcode. Initially this function emulated
214 // /etc/fonts/conf.d/30-metric-aliases.conf
215 // from my Ubuntu system, but we're better off being very conservative.
216
217 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
218 // Arial, Times New Roman and Courier New with a character repertoire
219 // much larger than Liberation. Note that Cousine is metrically
220 // compatible with Courier New, but the former is sans-serif while
221 // the latter is serif.
222
223
224 struct FontEquivMap {
225 FontEquivClass clazz;
226 const char name[40];
227 };
228
229 static const FontEquivMap kFontEquivMap[] = {
230 { SANS, "Arial" },
231 { SANS, "Arimo" },
232 { SANS, "Liberation Sans" },
233
234 { SERIF, "Times New Roman" },
235 { SERIF, "Tinos" },
236 { SERIF, "Liberation Serif" },
237
238 { MONO, "Courier New" },
239 { MONO, "Cousine" },
240 { MONO, "Liberation Mono" },
241
242 { SYMBOL, "Symbol" },
243 { SYMBOL, "Symbol Neu" },
244
245 // MS Pゴシック
246 { PGOTHIC, "MS PGothic" },
247 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
248 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshin7476cf52014-09-09 12:30:57 -0700249 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000250 { PGOTHIC, "IPAPGothic" },
251 { PGOTHIC, "MotoyaG04Gothic" },
252
253 // MS ゴシック
254 { GOTHIC, "MS Gothic" },
255 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
256 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshindd4e5682015-05-12 12:08:36 -0700257 { GOTHIC, "Noto Sans Mono CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000258 { GOTHIC, "IPAGothic" },
259 { GOTHIC, "MotoyaG04GothicMono" },
260
261 // MS P明朝
262 { PMINCHO, "MS PMincho" },
263 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
264 "\xe6\x98\x8e\xe6\x9c\x9d"},
265 { PMINCHO, "IPAPMincho" },
266 { PMINCHO, "MotoyaG04Mincho" },
267
268 // MS 明朝
269 { MINCHO, "MS Mincho" },
270 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
271 { MINCHO, "IPAMincho" },
272 { MINCHO, "MotoyaG04MinchoMono" },
273
274 // 宋体
275 { SIMSUN, "Simsun" },
276 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
277 { SIMSUN, "MSung GB18030" },
278 { SIMSUN, "Song ASC" },
279
280 // 新宋体
281 { NSIMSUN, "NSimsun" },
282 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
283 { NSIMSUN, "MSung GB18030" },
284 { NSIMSUN, "N Song ASC" },
285
286 // 黑体
287 { SIMHEI, "Simhei" },
288 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700289 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000290 { SIMHEI, "MYingHeiGB18030" },
291 { SIMHEI, "MYingHeiB5HK" },
292
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000293 // 新細明體
294 { PMINGLIU, "PMingLiU"},
295 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
296 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000297
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000298 // 細明體
299 { MINGLIU, "MingLiU"},
300 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
301 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000302
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000303 // 新細明體
304 { PMINGLIUHK, "PMingLiU_HKSCS"},
305 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
306 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000307
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000308 // 細明體
309 { MINGLIUHK, "MingLiU_HKSCS"},
310 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
311 { MINGLIUHK, "MSung B5HK"},
312
313 // Cambria
314 { CAMBRIA, "Cambria" },
315 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000316
317 // Calibri
318 { CALIBRI, "Calibri" },
319 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000320 };
321
322 static const size_t kFontCount =
323 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
324
325 // TODO(jungshik): If this loop turns out to be hot, turn
326 // the array to a static (hash)map to speed it up.
327 for (size_t i = 0; i < kFontCount; ++i) {
328 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
329 return kFontEquivMap[i].clazz;
330 }
331 return OTHER;
332}
333
334
335// Return true if |font_a| and |font_b| are visually and at the metrics
336// level interchangeable.
337bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
338{
339 FontEquivClass class_a = GetFontEquivClass(font_a);
340 FontEquivClass class_b = GetFontEquivClass(font_b);
341
342 return class_a != OTHER && class_a == class_b;
343}
344
reed@google.comb1c65b62013-02-26 15:50:51 +0000345// Normally we only return exactly the font asked for. In last-resort
346// cases, the request either doesn't specify a font or is one of the
347// basic font names like "Sans", "Serif" or "Monospace". This function
348// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700349bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000350 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700351 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000352 strcasecmp(family_cstr, "sans") == 0 ||
353 strcasecmp(family_cstr, "serif") == 0 ||
354 strcasecmp(family_cstr, "monospace") == 0;
355}
356
reed@google.comb1c65b62013-02-26 15:50:51 +0000357// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
bungeman11a77c62016-04-12 13:45:06 -0700358static int get_int(FcPattern* pattern, const char object[], int missing) {
359 int value;
360 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
361 return missing;
362 }
363 return value;
364}
365
366static int map_range(SkFixed value,
367 SkFixed old_min, SkFixed old_max,
368 SkFixed new_min, SkFixed new_max)
369{
370 SkASSERT(old_min < old_max);
371 SkASSERT(new_min <= new_max);
372 return new_min + SkMulDiv(value - old_min, new_max - new_min, old_max - old_min);
373}
374
375struct MapRanges {
376 SkFixed old_val;
377 SkFixed new_val;
378};
379
380static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int rangesCount) {
381 // -Inf to [0]
382 if (val < ranges[0].old_val) {
383 return ranges[0].new_val;
384 }
385
386 // Linear from [i] to [i+1]
387 for (int i = 0; i < rangesCount - 1; ++i) {
388 if (val < ranges[i+1].old_val) {
389 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
390 ranges[i].new_val, ranges[i+1].new_val);
391 }
392 }
393
394 // From [n] to +Inf
395 // if (fcweight < Inf)
396 return ranges[rangesCount-1].new_val;
397}
398
399static int map_ranges(int val, MapRanges const ranges[], int rangesCount) {
400 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesCount));
401}
402
403template<int n> struct SkTFixed {
404 static_assert(-32768 <= n && n <= 32767, "SkTFixed_n_not_in_range");
405 static const SkFixed value = static_cast<SkFixed>(n << 16);
406};
407
408static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
409 typedef SkFontStyle SkFS;
410
411 static const MapRanges weightRanges[] = {
412 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value },
413 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weight>::value },
414 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value },
415 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::value },
416 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::value },
417 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>::value },
418 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
419 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
420 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
421 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<1000>::value },
422 };
423 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
424 weightRanges, SK_ARRAY_COUNT(weightRanges));
425
426 static const MapRanges widthRanges[] = {
427 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondensed_Width>::value },
428 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondensed_Width>::value },
429 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>::value },
430 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed_Width>::value },
431 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value },
432 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_Width>::value },
433 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::value },
434 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded_Width>::value },
435 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltaExpanded_Width>::value },
436 };
437 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
438 widthRanges, SK_ARRAY_COUNT(widthRanges));
439
bungemanb4bb7d82016-04-27 10:21:04 -0700440 SkFS::Slant slant = SkFS::kUpright_Slant;
441 switch (get_int(pattern, FC_SLANT, FC_SLANT_ROMAN)) {
442 case FC_SLANT_ROMAN: slant = SkFS::kUpright_Slant; break;
443 case FC_SLANT_ITALIC : slant = SkFS::kItalic_Slant ; break;
444 case FC_SLANT_OBLIQUE: slant = SkFS::kOblique_Slant; break;
445 default: SkASSERT(false); break;
446 }
bungeman11a77c62016-04-12 13:45:06 -0700447
448 return SkFontStyle(weight, width, slant);
449}
450
451static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
452 typedef SkFontStyle SkFS;
453
454 static const MapRanges weightRanges[] = {
455 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value },
456 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIGHT>::value },
457 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value },
458 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::value },
459 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::value },
460 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>::value },
461 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
462 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
463 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
464 { SkTFixed<1000>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
465 };
466 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
467
468 static const MapRanges widthRanges[] = {
469 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRACONDENSED>::value },
470 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRACONDENSED>::value },
471 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>::value },
472 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICONDENSED>::value },
473 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value },
474 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPANDED>::value },
475 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::value },
476 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value },
477 { SkTFixed<SkFS::kUltaExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value },
478 };
479 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges));
480
bungemanb4bb7d82016-04-27 10:21:04 -0700481 int slant = FC_SLANT_ROMAN;
482 switch (style.slant()) {
483 case SkFS::kUpright_Slant: slant = FC_SLANT_ROMAN ; break;
484 case SkFS::kItalic_Slant : slant = FC_SLANT_ITALIC ; break;
485 case SkFS::kOblique_Slant: slant = FC_SLANT_OBLIQUE; break;
486 default: SkASSERT(false); break;
487 }
488
bungeman11a77c62016-04-12 13:45:06 -0700489 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
bungemanb4bb7d82016-04-27 10:21:04 -0700490 FcPatternAddInteger(pattern, FC_WIDTH , width);
491 FcPatternAddInteger(pattern, FC_SLANT , slant);
bungeman11a77c62016-04-12 13:45:06 -0700492}
493
reed@google.comb1c65b62013-02-26 15:50:51 +0000494} // anonymous namespace
495
496///////////////////////////////////////////////////////////////////////////////
497
reed@google.comf71a2332013-02-27 19:06:30 +0000498#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000499
reed@google.comf71a2332013-02-27 19:06:30 +0000500SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
bungeman02657072016-05-02 11:54:13 -0700501 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000502
reed@google.comb1c65b62013-02-26 15:50:51 +0000503 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000504
505 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000506}
507
508SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
509}
510
benjaminwagner2211a7b2015-12-01 11:12:05 -0800511bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
512 if (access(filename, R_OK) != 0) {
513 return false;
514 }
515 return true;
516}
517
518bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
519#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
520 FcBool is_scalable;
521 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
522 || !is_scalable) {
523 return false;
524 }
525#endif
526
527 // fontconfig can also return fonts which are unreadable
528 const char* c_filename = get_name(pattern, FC_FILE);
529 if (!c_filename) {
530 return false;
531 }
532 return this->isAccessible(c_filename);
533}
534
535// Find matching font from |font_set| for the given font family.
536FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
537 const char* post_config_family,
538 const SkString& family) {
539 // Older versions of fontconfig have a bug where they cannot select
540 // only scalable fonts so we have to manually filter the results.
541 FcPattern* match = nullptr;
542 for (int i = 0; i < font_set->nfont; ++i) {
543 FcPattern* current = font_set->fonts[i];
544 if (this->isValidPattern(current)) {
545 match = current;
546 break;
547 }
548 }
549
550 if (match && !IsFallbackFontAllowed(family)) {
551 bool acceptable_substitute = false;
552 for (int id = 0; id < 255; ++id) {
553 const char* post_match_family = get_name(match, FC_FAMILY, id);
554 if (!post_match_family)
555 break;
556 acceptable_substitute =
557 (strcasecmp(post_config_family, post_match_family) == 0 ||
558 // Workaround for Issue 12530:
559 // requested family: "Bitstream Vera Sans"
560 // post_config_family: "Arial"
561 // post_match_family: "Bitstream Vera Sans"
562 // -> We should treat this case as a good match.
563 strcasecmp(family.c_str(), post_match_family) == 0) ||
564 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
565 if (acceptable_substitute)
566 break;
567 }
568 if (!acceptable_substitute)
569 return nullptr;
570 }
571
572 return match;
573}
574
bungeman11a77c62016-04-12 13:45:06 -0700575bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
576 SkFontStyle style,
577 FontIdentity* outIdentity,
578 SkString* outFamilyName,
579 SkFontStyle* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700580 SkString familyStr(familyName ? familyName : "");
581 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000582 return false;
583 }
584
bungeman02657072016-05-02 11:54:13 -0700585 FCLocker lock;
reed@google.comb1c65b62013-02-26 15:50:51 +0000586
reed@google.comb1c65b62013-02-26 15:50:51 +0000587 FcPattern* pattern = FcPatternCreate();
588
reed@google.comee619a02013-02-26 22:58:09 +0000589 if (familyName) {
590 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
591 }
bungeman11a77c62016-04-12 13:45:06 -0700592 fcpattern_from_skfontstyle(style, pattern);
bungeman11a77c62016-04-12 13:45:06 -0700593
reed@google.comb1c65b62013-02-26 15:50:51 +0000594 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
595
halcanary96fcdcc2015-08-27 07:41:13 -0700596 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000597 FcDefaultSubstitute(pattern);
598
599 // Font matching:
600 // CSS often specifies a fallback list of families:
601 // font-family: a, b, c, serif;
602 // However, fontconfig will always do its best to find *a* font when asked
603 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700604 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000605 // and lets WebKit know to try the next CSS family name. However, fontconfig
606 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
607 // wish to support that.
608 //
609 // Thus, if a specific family is requested we set @family_requested. Then we
610 // record two strings: the family name after config processing and the
611 // family name after resolving. If the two are equal, it's a good match.
612 //
613 // So consider the case where a user has mapped Arial to Helvetica in their
614 // config.
615 // requested family: "Arial"
616 // post_config_family: "Helvetica"
617 // post_match_family: "Helvetica"
618 // -> good match
619 //
620 // and for a missing font:
621 // requested family: "Monaco"
622 // post_config_family: "Monaco"
623 // post_match_family: "Times New Roman"
624 // -> BAD match
625 //
626 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000627
628 const char* post_config_family = get_name(pattern, FC_FAMILY);
629 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000630 // we can just continue with an empty name, e.g. default font
631 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000632 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000633
634 FcResult result;
635 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
636 if (!font_set) {
637 FcPatternDestroy(pattern);
638 return false;
639 }
640
benjaminwagner2211a7b2015-12-01 11:12:05 -0800641 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000642 if (!match) {
643 FcPatternDestroy(pattern);
644 FcFontSetDestroy(font_set);
645 return false;
646 }
647
648 FcPatternDestroy(pattern);
649
reed@google.comf71a2332013-02-27 19:06:30 +0000650 // From here out we just extract our results from 'match'
651
reed@google.come49d67e2013-04-22 18:00:06 +0000652 post_config_family = get_name(match, FC_FAMILY);
653 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000654 FcFontSetDestroy(font_set);
655 return false;
656 }
657
reed@google.come49d67e2013-04-22 18:00:06 +0000658 const char* c_filename = get_name(match, FC_FILE);
659 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000660 FcFontSetDestroy(font_set);
661 return false;
662 }
reed@google.comf71a2332013-02-27 19:06:30 +0000663
reed@google.comb1c65b62013-02-26 15:50:51 +0000664 int face_index;
665 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
666 FcFontSetDestroy(font_set);
667 return false;
668 }
669
reed@google.comb1c65b62013-02-26 15:50:51 +0000670 FcFontSetDestroy(font_set);
671
reed@google.comf71a2332013-02-27 19:06:30 +0000672 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000673 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000674 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000675 }
reed@google.comf71a2332013-02-27 19:06:30 +0000676 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000677 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000678 }
reed@google.comf71a2332013-02-27 19:06:30 +0000679 if (outStyle) {
bungemaned2edab2016-04-13 05:23:35 -0700680 *outStyle = skfontstyle_from_fcpattern(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000681 }
reed@google.comee619a02013-02-26 22:58:09 +0000682 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000683}
684
bungeman5f213d92015-01-27 05:39:10 -0800685SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000686 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000687}
reed@google.com027fd202013-04-19 20:45:30 +0000688
689///////////////////////////////////////////////////////////////////////////////
690
reed@google.com027fd202013-04-19 20:45:30 +0000691static bool find_name(const SkTDArray<const char*>& list, const char* str) {
692 int count = list.count();
693 for (int i = 0; i < count; ++i) {
694 if (!strcmp(list[i], str)) {
695 return true;
696 }
697 }
698 return false;
699}
700
701SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
bungeman02657072016-05-02 11:54:13 -0700702 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000703
reed@google.com027fd202013-04-19 20:45:30 +0000704 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000705 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
halcanary96fcdcc2015-08-27 07:41:13 -0700706 if (nullptr == pat) {
707 return nullptr;
bungeman@google.com02dd6882013-07-22 14:10:08 +0000708 }
709
710 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
711 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
halcanary96fcdcc2015-08-27 07:41:13 -0700712 if (nullptr == os) {
713 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000714 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000715
halcanary96fcdcc2015-08-27 07:41:13 -0700716 FcFontSet* fs = FcFontList(nullptr, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000717 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
halcanary96fcdcc2015-08-27 07:41:13 -0700718 if (nullptr == fs) {
719 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000720 }
721
722 SkTDArray<const char*> names;
723 SkTDArray<size_t> sizes;
724 for (int i = 0; i < fs->nfont; ++i) {
725 FcPattern* match = fs->fonts[i];
726 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000727 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000728 *names.append() = famName;
729 *sizes.append() = strlen(famName) + 1;
730 }
731 }
732
reed@google.com027fd202013-04-19 20:45:30 +0000733 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
734 sizes.begin(), names.count());
735}