blob: c8bafc712a74f93dd2af4cb0f4d410bf3061b112 [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.
reed086eea92016-05-04 17:12:46 -070035SK_DECLARE_STATIC_MUTEX(gFCMutex);
bungeman02657072016-05-02 11:54:13 -070036
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
bungeman875b8f62016-09-16 13:19:49 -0700164static const char* get_string(FcPattern* pattern, const char field[], int index = 0) {
reed@google.come49d67e2013-04-22 18:00:06 +0000165 const char* name;
bungeman875b8f62016-09-16 13:19:49 -0700166 if (FcPatternGetString(pattern, field, index, (FcChar8**)&name) != FcResultMatch) {
halcanary96fcdcc2015-08-27 07:41:13 -0700167 name = nullptr;
reed@google.come49d67e2013-04-22 18:00:06 +0000168 }
169 return name;
170}
171
172///////////////////////////////////////////////////////////////////////////////
173
reed@google.comb1c65b62013-02-26 15:50:51 +0000174namespace {
175
176// Equivalence classes, used to match the Liberation and other fonts
177// with their metric-compatible replacements. See the discussion in
178// GetFontEquivClass().
179enum FontEquivClass
180{
181 OTHER,
182 SANS,
183 SERIF,
184 MONO,
185 SYMBOL,
186 PGOTHIC,
187 GOTHIC,
188 PMINCHO,
189 MINCHO,
190 SIMSUN,
191 NSIMSUN,
192 SIMHEI,
193 PMINGLIU,
194 MINGLIU,
195 PMINGLIUHK,
196 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000197 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000198 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000199};
200
201// Match the font name against a whilelist of fonts, returning the equivalence
202// class.
203FontEquivClass GetFontEquivClass(const char* fontname)
204{
205 // It would be nice for fontconfig to tell us whether a given suggested
206 // replacement is a "strong" match (that is, an equivalent font) or
207 // a "weak" match (that is, fontconfig's next-best attempt at finding a
208 // substitute). However, I played around with the fontconfig API for
209 // a good few hours and could not make it reveal this information.
210 //
211 // So instead, we hardcode. Initially this function emulated
212 // /etc/fonts/conf.d/30-metric-aliases.conf
213 // from my Ubuntu system, but we're better off being very conservative.
214
215 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
216 // Arial, Times New Roman and Courier New with a character repertoire
217 // much larger than Liberation. Note that Cousine is metrically
218 // compatible with Courier New, but the former is sans-serif while
219 // the latter is serif.
220
221
222 struct FontEquivMap {
223 FontEquivClass clazz;
224 const char name[40];
225 };
226
227 static const FontEquivMap kFontEquivMap[] = {
228 { SANS, "Arial" },
229 { SANS, "Arimo" },
230 { SANS, "Liberation Sans" },
231
232 { SERIF, "Times New Roman" },
233 { SERIF, "Tinos" },
234 { SERIF, "Liberation Serif" },
235
236 { MONO, "Courier New" },
237 { MONO, "Cousine" },
238 { MONO, "Liberation Mono" },
239
240 { SYMBOL, "Symbol" },
241 { SYMBOL, "Symbol Neu" },
242
243 // MS Pゴシック
244 { PGOTHIC, "MS PGothic" },
245 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
246 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshin7476cf52014-09-09 12:30:57 -0700247 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000248 { PGOTHIC, "IPAPGothic" },
249 { PGOTHIC, "MotoyaG04Gothic" },
250
251 // MS ゴシック
252 { GOTHIC, "MS Gothic" },
253 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
254 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshindd4e5682015-05-12 12:08:36 -0700255 { GOTHIC, "Noto Sans Mono CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000256 { GOTHIC, "IPAGothic" },
257 { GOTHIC, "MotoyaG04GothicMono" },
258
259 // MS P明朝
260 { PMINCHO, "MS PMincho" },
261 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
262 "\xe6\x98\x8e\xe6\x9c\x9d"},
263 { PMINCHO, "IPAPMincho" },
264 { PMINCHO, "MotoyaG04Mincho" },
265
266 // MS 明朝
267 { MINCHO, "MS Mincho" },
268 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
269 { MINCHO, "IPAMincho" },
270 { MINCHO, "MotoyaG04MinchoMono" },
271
272 // 宋体
273 { SIMSUN, "Simsun" },
274 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
275 { SIMSUN, "MSung GB18030" },
276 { SIMSUN, "Song ASC" },
277
278 // 新宋体
279 { NSIMSUN, "NSimsun" },
280 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
281 { NSIMSUN, "MSung GB18030" },
282 { NSIMSUN, "N Song ASC" },
283
284 // 黑体
285 { SIMHEI, "Simhei" },
286 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700287 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000288 { SIMHEI, "MYingHeiGB18030" },
289 { SIMHEI, "MYingHeiB5HK" },
290
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000291 // 新細明體
292 { PMINGLIU, "PMingLiU"},
293 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
294 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000295
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000296 // 細明體
297 { MINGLIU, "MingLiU"},
298 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
299 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000300
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000301 // 新細明體
302 { PMINGLIUHK, "PMingLiU_HKSCS"},
303 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
304 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000305
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000306 // 細明體
307 { MINGLIUHK, "MingLiU_HKSCS"},
308 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
309 { MINGLIUHK, "MSung B5HK"},
310
311 // Cambria
312 { CAMBRIA, "Cambria" },
313 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000314
315 // Calibri
316 { CALIBRI, "Calibri" },
317 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000318 };
319
320 static const size_t kFontCount =
321 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
322
323 // TODO(jungshik): If this loop turns out to be hot, turn
324 // the array to a static (hash)map to speed it up.
325 for (size_t i = 0; i < kFontCount; ++i) {
326 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
327 return kFontEquivMap[i].clazz;
328 }
329 return OTHER;
330}
331
332
333// Return true if |font_a| and |font_b| are visually and at the metrics
334// level interchangeable.
335bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
336{
337 FontEquivClass class_a = GetFontEquivClass(font_a);
338 FontEquivClass class_b = GetFontEquivClass(font_b);
339
340 return class_a != OTHER && class_a == class_b;
341}
342
reed@google.comb1c65b62013-02-26 15:50:51 +0000343// Normally we only return exactly the font asked for. In last-resort
344// cases, the request either doesn't specify a font or is one of the
345// basic font names like "Sans", "Serif" or "Monospace". This function
346// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700347bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000348 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700349 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000350 strcasecmp(family_cstr, "sans") == 0 ||
351 strcasecmp(family_cstr, "serif") == 0 ||
352 strcasecmp(family_cstr, "monospace") == 0;
353}
354
reed@google.comb1c65b62013-02-26 15:50:51 +0000355// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
bungeman11a77c62016-04-12 13:45:06 -0700356static int get_int(FcPattern* pattern, const char object[], int missing) {
357 int value;
358 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
359 return missing;
360 }
361 return value;
362}
363
364static int map_range(SkFixed value,
365 SkFixed old_min, SkFixed old_max,
366 SkFixed new_min, SkFixed new_max)
367{
368 SkASSERT(old_min < old_max);
369 SkASSERT(new_min <= new_max);
370 return new_min + SkMulDiv(value - old_min, new_max - new_min, old_max - old_min);
371}
372
373struct MapRanges {
374 SkFixed old_val;
375 SkFixed new_val;
376};
377
378static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int rangesCount) {
379 // -Inf to [0]
380 if (val < ranges[0].old_val) {
381 return ranges[0].new_val;
382 }
383
384 // Linear from [i] to [i+1]
385 for (int i = 0; i < rangesCount - 1; ++i) {
386 if (val < ranges[i+1].old_val) {
387 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
388 ranges[i].new_val, ranges[i+1].new_val);
389 }
390 }
391
392 // From [n] to +Inf
393 // if (fcweight < Inf)
394 return ranges[rangesCount-1].new_val;
395}
396
397static int map_ranges(int val, MapRanges const ranges[], int rangesCount) {
398 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesCount));
399}
400
401template<int n> struct SkTFixed {
402 static_assert(-32768 <= n && n <= 32767, "SkTFixed_n_not_in_range");
403 static const SkFixed value = static_cast<SkFixed>(n << 16);
404};
405
406static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
407 typedef SkFontStyle SkFS;
408
409 static const MapRanges weightRanges[] = {
410 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value },
411 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weight>::value },
412 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value },
413 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::value },
414 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::value },
415 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>::value },
416 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
417 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
418 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
bungeman6e45bda2016-07-25 15:11:49 -0700419 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<SkFS::kExtraBlack_Weight>::value },
bungeman11a77c62016-04-12 13:45:06 -0700420 };
421 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
422 weightRanges, SK_ARRAY_COUNT(weightRanges));
423
424 static const MapRanges widthRanges[] = {
425 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondensed_Width>::value },
426 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondensed_Width>::value },
427 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>::value },
428 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed_Width>::value },
429 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value },
430 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_Width>::value },
431 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::value },
432 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded_Width>::value },
bungemand783e082016-08-01 12:37:13 -0700433 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltraExpanded_Width>::value },
bungeman11a77c62016-04-12 13:45:06 -0700434 };
435 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
436 widthRanges, SK_ARRAY_COUNT(widthRanges));
437
bungemanb4bb7d82016-04-27 10:21:04 -0700438 SkFS::Slant slant = SkFS::kUpright_Slant;
439 switch (get_int(pattern, FC_SLANT, FC_SLANT_ROMAN)) {
440 case FC_SLANT_ROMAN: slant = SkFS::kUpright_Slant; break;
441 case FC_SLANT_ITALIC : slant = SkFS::kItalic_Slant ; break;
442 case FC_SLANT_OBLIQUE: slant = SkFS::kOblique_Slant; break;
443 default: SkASSERT(false); break;
444 }
bungeman11a77c62016-04-12 13:45:06 -0700445
446 return SkFontStyle(weight, width, slant);
447}
448
449static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
450 typedef SkFontStyle SkFS;
451
452 static const MapRanges weightRanges[] = {
453 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value },
454 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIGHT>::value },
455 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value },
456 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::value },
457 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::value },
458 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>::value },
459 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
460 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
461 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
bungeman6e45bda2016-07-25 15:11:49 -0700462 { SkTFixed<SkFS::kExtraBlack_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
bungeman11a77c62016-04-12 13:45:06 -0700463 };
464 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
465
466 static const MapRanges widthRanges[] = {
467 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRACONDENSED>::value },
468 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRACONDENSED>::value },
469 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>::value },
470 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICONDENSED>::value },
471 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value },
472 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPANDED>::value },
473 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::value },
474 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value },
bungemand783e082016-08-01 12:37:13 -0700475 { SkTFixed<SkFS::kUltraExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value },
bungeman11a77c62016-04-12 13:45:06 -0700476 };
477 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges));
478
bungemanb4bb7d82016-04-27 10:21:04 -0700479 int slant = FC_SLANT_ROMAN;
480 switch (style.slant()) {
481 case SkFS::kUpright_Slant: slant = FC_SLANT_ROMAN ; break;
482 case SkFS::kItalic_Slant : slant = FC_SLANT_ITALIC ; break;
483 case SkFS::kOblique_Slant: slant = FC_SLANT_OBLIQUE; break;
484 default: SkASSERT(false); break;
485 }
486
bungeman11a77c62016-04-12 13:45:06 -0700487 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
bungemanb4bb7d82016-04-27 10:21:04 -0700488 FcPatternAddInteger(pattern, FC_WIDTH , width);
489 FcPatternAddInteger(pattern, FC_SLANT , slant);
bungeman11a77c62016-04-12 13:45:06 -0700490}
491
reed@google.comb1c65b62013-02-26 15:50:51 +0000492} // anonymous namespace
493
494///////////////////////////////////////////////////////////////////////////////
495
reed@google.comf71a2332013-02-27 19:06:30 +0000496#define kMaxFontFamilyLength 2048
drott358f93d2016-08-26 10:08:45 -0700497#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
498const char* kFontFormatTrueType = "TrueType";
499const char* kFontFormatCFF = "CFF";
500#endif
reed@google.comb1c65b62013-02-26 15:50:51 +0000501
reed@google.comf71a2332013-02-27 19:06:30 +0000502SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
bungeman02657072016-05-02 11:54:13 -0700503 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000504
reed@google.comb1c65b62013-02-26 15:50:51 +0000505 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000506
507 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000508}
509
510SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
511}
512
benjaminwagner2211a7b2015-12-01 11:12:05 -0800513bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
514 if (access(filename, R_OK) != 0) {
515 return false;
516 }
517 return true;
518}
519
520bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
drott358f93d2016-08-26 10:08:45 -0700521#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
bungeman875b8f62016-09-16 13:19:49 -0700522 const char* font_format = get_string(pattern, FC_FONTFORMAT);
drott358f93d2016-08-26 10:08:45 -0700523 if (font_format
524 && strcmp(font_format, kFontFormatTrueType) != 0
525 && strcmp(font_format, kFontFormatCFF) != 0)
526 {
527 return false;
528 }
529#endif
530
benjaminwagner2211a7b2015-12-01 11:12:05 -0800531 // fontconfig can also return fonts which are unreadable
bungeman875b8f62016-09-16 13:19:49 -0700532 const char* c_filename = get_string(pattern, FC_FILE);
benjaminwagner2211a7b2015-12-01 11:12:05 -0800533 if (!c_filename) {
534 return false;
535 }
536 return this->isAccessible(c_filename);
537}
538
539// Find matching font from |font_set| for the given font family.
540FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
541 const char* post_config_family,
542 const SkString& family) {
543 // Older versions of fontconfig have a bug where they cannot select
544 // only scalable fonts so we have to manually filter the results.
545 FcPattern* match = nullptr;
546 for (int i = 0; i < font_set->nfont; ++i) {
547 FcPattern* current = font_set->fonts[i];
548 if (this->isValidPattern(current)) {
549 match = current;
550 break;
551 }
552 }
553
554 if (match && !IsFallbackFontAllowed(family)) {
555 bool acceptable_substitute = false;
556 for (int id = 0; id < 255; ++id) {
bungeman875b8f62016-09-16 13:19:49 -0700557 const char* post_match_family = get_string(match, FC_FAMILY, id);
benjaminwagner2211a7b2015-12-01 11:12:05 -0800558 if (!post_match_family)
559 break;
560 acceptable_substitute =
561 (strcasecmp(post_config_family, post_match_family) == 0 ||
562 // Workaround for Issue 12530:
563 // requested family: "Bitstream Vera Sans"
564 // post_config_family: "Arial"
565 // post_match_family: "Bitstream Vera Sans"
566 // -> We should treat this case as a good match.
567 strcasecmp(family.c_str(), post_match_family) == 0) ||
568 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
569 if (acceptable_substitute)
570 break;
571 }
572 if (!acceptable_substitute)
573 return nullptr;
574 }
575
576 return match;
577}
578
bungeman11a77c62016-04-12 13:45:06 -0700579bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
580 SkFontStyle style,
581 FontIdentity* outIdentity,
582 SkString* outFamilyName,
583 SkFontStyle* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700584 SkString familyStr(familyName ? familyName : "");
585 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000586 return false;
587 }
588
bungeman02657072016-05-02 11:54:13 -0700589 FCLocker lock;
reed@google.comb1c65b62013-02-26 15:50:51 +0000590
reed@google.comb1c65b62013-02-26 15:50:51 +0000591 FcPattern* pattern = FcPatternCreate();
592
reed@google.comee619a02013-02-26 22:58:09 +0000593 if (familyName) {
594 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
595 }
bungeman11a77c62016-04-12 13:45:06 -0700596 fcpattern_from_skfontstyle(style, pattern);
bungeman11a77c62016-04-12 13:45:06 -0700597
reed@google.comb1c65b62013-02-26 15:50:51 +0000598 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
599
drott358f93d2016-08-26 10:08:45 -0700600#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
601 FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8*>(kFontFormatTrueType));
602 FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8*>(kFontFormatCFF));
603#endif
604
halcanary96fcdcc2015-08-27 07:41:13 -0700605 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000606 FcDefaultSubstitute(pattern);
607
608 // Font matching:
609 // CSS often specifies a fallback list of families:
610 // font-family: a, b, c, serif;
611 // However, fontconfig will always do its best to find *a* font when asked
612 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700613 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000614 // and lets WebKit know to try the next CSS family name. However, fontconfig
615 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
616 // wish to support that.
617 //
618 // Thus, if a specific family is requested we set @family_requested. Then we
619 // record two strings: the family name after config processing and the
620 // family name after resolving. If the two are equal, it's a good match.
621 //
622 // So consider the case where a user has mapped Arial to Helvetica in their
623 // config.
624 // requested family: "Arial"
625 // post_config_family: "Helvetica"
626 // post_match_family: "Helvetica"
627 // -> good match
628 //
629 // and for a missing font:
630 // requested family: "Monaco"
631 // post_config_family: "Monaco"
632 // post_match_family: "Times New Roman"
633 // -> BAD match
634 //
635 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000636
bungeman875b8f62016-09-16 13:19:49 -0700637 const char* post_config_family = get_string(pattern, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000638 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000639 // we can just continue with an empty name, e.g. default font
640 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000641 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000642
643 FcResult result;
bungeman6d195b22016-05-24 08:08:20 -0700644 FcFontSet* font_set = FcFontSort(nullptr, pattern, 0, nullptr, &result);
reed@google.comb1c65b62013-02-26 15:50:51 +0000645 if (!font_set) {
646 FcPatternDestroy(pattern);
647 return false;
648 }
649
benjaminwagner2211a7b2015-12-01 11:12:05 -0800650 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000651 if (!match) {
652 FcPatternDestroy(pattern);
653 FcFontSetDestroy(font_set);
654 return false;
655 }
656
657 FcPatternDestroy(pattern);
658
reed@google.comf71a2332013-02-27 19:06:30 +0000659 // From here out we just extract our results from 'match'
660
bungeman875b8f62016-09-16 13:19:49 -0700661 post_config_family = get_string(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000662 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000663 FcFontSetDestroy(font_set);
664 return false;
665 }
666
bungeman875b8f62016-09-16 13:19:49 -0700667 const char* c_filename = get_string(match, FC_FILE);
reed@google.come49d67e2013-04-22 18:00:06 +0000668 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000669 FcFontSetDestroy(font_set);
670 return false;
671 }
reed@google.comf71a2332013-02-27 19:06:30 +0000672
bungeman875b8f62016-09-16 13:19:49 -0700673 int face_index = get_int(match, FC_INDEX, 0);
reed@google.comb1c65b62013-02-26 15:50:51 +0000674
reed@google.comb1c65b62013-02-26 15:50:51 +0000675 FcFontSetDestroy(font_set);
676
reed@google.comf71a2332013-02-27 19:06:30 +0000677 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000678 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000679 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000680 }
reed@google.comf71a2332013-02-27 19:06:30 +0000681 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000682 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000683 }
reed@google.comf71a2332013-02-27 19:06:30 +0000684 if (outStyle) {
bungemaned2edab2016-04-13 05:23:35 -0700685 *outStyle = skfontstyle_from_fcpattern(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000686 }
reed@google.comee619a02013-02-26 22:58:09 +0000687 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000688}
689
bungeman5f213d92015-01-27 05:39:10 -0800690SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
bungemanf93d7112016-09-16 06:24:20 -0700691 return SkStream::MakeFromFile(identity.fString.c_str()).release();
reed@google.comb1c65b62013-02-26 15:50:51 +0000692}
reed@google.com027fd202013-04-19 20:45:30 +0000693
694///////////////////////////////////////////////////////////////////////////////
695
reed@google.com027fd202013-04-19 20:45:30 +0000696static bool find_name(const SkTDArray<const char*>& list, const char* str) {
697 int count = list.count();
698 for (int i = 0; i < count; ++i) {
699 if (!strcmp(list[i], str)) {
700 return true;
701 }
702 }
703 return false;
704}
705
bungemanfeb3c1a2016-08-05 06:51:50 -0700706sk_sp<SkDataTable> SkFontConfigInterfaceDirect::getFamilyNames() {
bungeman02657072016-05-02 11:54:13 -0700707 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000708
reed@google.com027fd202013-04-19 20:45:30 +0000709 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000710 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
halcanary96fcdcc2015-08-27 07:41:13 -0700711 if (nullptr == pat) {
712 return nullptr;
bungeman@google.com02dd6882013-07-22 14:10:08 +0000713 }
714
715 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
716 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
halcanary96fcdcc2015-08-27 07:41:13 -0700717 if (nullptr == os) {
718 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000719 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000720
halcanary96fcdcc2015-08-27 07:41:13 -0700721 FcFontSet* fs = FcFontList(nullptr, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000722 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
halcanary96fcdcc2015-08-27 07:41:13 -0700723 if (nullptr == fs) {
724 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000725 }
726
727 SkTDArray<const char*> names;
728 SkTDArray<size_t> sizes;
729 for (int i = 0; i < fs->nfont; ++i) {
730 FcPattern* match = fs->fonts[i];
bungeman875b8f62016-09-16 13:19:49 -0700731 const char* famName = get_string(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000732 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000733 *names.append() = famName;
734 *sizes.append() = strlen(famName) + 1;
735 }
736 }
737
bungemanfeb3c1a2016-08-05 06:51:50 -0700738 return SkDataTable::MakeCopyArrays((const void*const*)names.begin(),
739 sizes.begin(), names.count());
reed@google.com027fd202013-04-19 20:45:30 +0000740}