blob: d552befb5ab73ef630011189de8dd4ad4a2de2a0 [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
Hal Canary95e3c052017-01-11 12:44:43 -050010#include "SkAutoMalloc.h"
reed@google.comf55061f2013-04-22 18:48:45 +000011#include "SkBuffer.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"
bungemanf20488b2015-07-29 11:49:40 -070022
23#include <fontconfig/fontconfig.h>
24#include <unistd.h>
reed@google.comb1c65b62013-02-26 15:50:51 +000025
bungeman02657072016-05-02 11:54:13 -070026#ifdef SK_DEBUG
27# include "SkTLS.h"
28#endif
29
30namespace {
31
32// Fontconfig is not threadsafe before 2.10.91. Before that, we lock with a global mutex.
33// See https://bug.skia.org/1497 for background.
reed086eea92016-05-04 17:12:46 -070034SK_DECLARE_STATIC_MUTEX(gFCMutex);
bungeman02657072016-05-02 11:54:13 -070035
36#ifdef SK_DEBUG
37void* CreateThreadFcLocked() { return new bool(false); }
38void DeleteThreadFcLocked(void* v) { delete static_cast<bool*>(v); }
39# define THREAD_FC_LOCKED \
40 static_cast<bool*>(SkTLS::Get(CreateThreadFcLocked, DeleteThreadFcLocked))
41#endif
42
43struct FCLocker {
44 // Assume FcGetVersion() has always been thread safe.
45
46 FCLocker() {
47 if (FcGetVersion() < 21091) {
48 gFCMutex.acquire();
49 } else {
50 SkDEBUGCODE(bool* threadLocked = THREAD_FC_LOCKED);
51 SkASSERT(false == *threadLocked);
52 SkDEBUGCODE(*threadLocked = true);
53 }
54 }
55
56 ~FCLocker() {
57 AssertHeld();
58 if (FcGetVersion() < 21091) {
59 gFCMutex.release();
60 } else {
61 SkDEBUGCODE(*THREAD_FC_LOCKED = false);
62 }
63 }
64
65 static void AssertHeld() { SkDEBUGCODE(
66 if (FcGetVersion() < 21091) {
67 gFCMutex.assertHeld();
68 } else {
69 SkASSERT(true == *THREAD_FC_LOCKED);
70 }
71 ) }
72};
73
74} // namespace
75
reed@google.comf55061f2013-04-22 18:48:45 +000076size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
77 size_t size = sizeof(fID) + sizeof(fTTCIndex);
78 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
79 size += sizeof(int32_t) + fString.size(); // store length+data
80 if (addr) {
81 SkWBuffer buffer(addr, size);
82
83 buffer.write32(fID);
84 buffer.write32(fTTCIndex);
85 buffer.write32(fString.size());
86 buffer.write32(fStyle.weight());
87 buffer.write32(fStyle.width());
88 buffer.write8(fStyle.slant());
89 buffer.write(fString.c_str(), fString.size());
90 buffer.padToAlign4();
91
92 SkASSERT(buffer.pos() == size);
93 }
94 return size;
95}
96
97size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
98 size_t size) {
99 SkRBuffer buffer(addr, size);
100
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +0000101 (void)buffer.readU32(&fID);
102 (void)buffer.readS32(&fTTCIndex);
103 uint32_t strLen, weight, width;
104 (void)buffer.readU32(&strLen);
105 (void)buffer.readU32(&weight);
106 (void)buffer.readU32(&width);
107 uint8_t u8;
108 (void)buffer.readU8(&u8);
109 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
reed@google.comf55061f2013-04-22 18:48:45 +0000110 fStyle = SkFontStyle(weight, width, slant);
111 fString.resize(strLen);
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +0000112 (void)buffer.read(fString.writable_str(), strLen);
reed@google.comf55061f2013-04-22 18:48:45 +0000113 buffer.skipToAlign4();
114
115 return buffer.pos(); // the actual number of bytes read
116}
117
118#ifdef SK_DEBUG
119static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
120 iden->fID = 10;
121 iden->fTTCIndex = 2;
122 iden->fString.set("Hello world");
123 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
124}
125
126static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
127 int initValue) {
128 SkFontConfigInterface::FontIdentity iden1;
129
halcanary96fcdcc2015-08-27 07:41:13 -0700130 size_t size0 = iden0.writeToMemory(nullptr);
reed@google.comf55061f2013-04-22 18:48:45 +0000131
132 SkAutoMalloc storage(size0);
133 memset(storage.get(), initValue, size0);
134
135 size_t size1 = iden0.writeToMemory(storage.get());
136 SkASSERT(size0 == size1);
137
138 SkASSERT(iden0 != iden1);
139 size_t size2 = iden1.readFromMemory(storage.get(), size1);
140 SkASSERT(size2 == size1);
141 SkASSERT(iden0 == iden1);
142}
143
144static void fontconfiginterface_unittest() {
145 SkFontConfigInterface::FontIdentity iden0, iden1;
146
147 SkASSERT(iden0 == iden1);
148
149 make_iden(&iden0);
150 SkASSERT(iden0 != iden1);
151
152 make_iden(&iden1);
153 SkASSERT(iden0 == iden1);
154
155 test_writeToMemory(iden0, 0);
156 test_writeToMemory(iden0, 0);
157}
158#endif
159
reed@google.com54c69142013-04-09 15:54:52 +0000160///////////////////////////////////////////////////////////////////////////////
161
halcanary96fcdcc2015-08-27 07:41:13 -0700162// Returns the string from the pattern, or nullptr
bungeman875b8f62016-09-16 13:19:49 -0700163static const char* get_string(FcPattern* pattern, const char field[], int index = 0) {
reed@google.come49d67e2013-04-22 18:00:06 +0000164 const char* name;
bungeman875b8f62016-09-16 13:19:49 -0700165 if (FcPatternGetString(pattern, field, index, (FcChar8**)&name) != FcResultMatch) {
halcanary96fcdcc2015-08-27 07:41:13 -0700166 name = nullptr;
reed@google.come49d67e2013-04-22 18:00:06 +0000167 }
168 return name;
169}
170
171///////////////////////////////////////////////////////////////////////////////
172
reed@google.comb1c65b62013-02-26 15:50:51 +0000173namespace {
174
175// Equivalence classes, used to match the Liberation and other fonts
176// with their metric-compatible replacements. See the discussion in
177// GetFontEquivClass().
178enum FontEquivClass
179{
180 OTHER,
181 SANS,
182 SERIF,
183 MONO,
184 SYMBOL,
185 PGOTHIC,
186 GOTHIC,
187 PMINCHO,
188 MINCHO,
189 SIMSUN,
190 NSIMSUN,
191 SIMHEI,
192 PMINGLIU,
193 MINGLIU,
194 PMINGLIUHK,
195 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000196 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000197 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000198};
199
200// Match the font name against a whilelist of fonts, returning the equivalence
201// class.
202FontEquivClass GetFontEquivClass(const char* fontname)
203{
204 // It would be nice for fontconfig to tell us whether a given suggested
205 // replacement is a "strong" match (that is, an equivalent font) or
206 // a "weak" match (that is, fontconfig's next-best attempt at finding a
207 // substitute). However, I played around with the fontconfig API for
208 // a good few hours and could not make it reveal this information.
209 //
210 // So instead, we hardcode. Initially this function emulated
211 // /etc/fonts/conf.d/30-metric-aliases.conf
212 // from my Ubuntu system, but we're better off being very conservative.
213
214 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
215 // Arial, Times New Roman and Courier New with a character repertoire
216 // much larger than Liberation. Note that Cousine is metrically
217 // compatible with Courier New, but the former is sans-serif while
218 // the latter is serif.
219
220
221 struct FontEquivMap {
222 FontEquivClass clazz;
223 const char name[40];
224 };
225
226 static const FontEquivMap kFontEquivMap[] = {
227 { SANS, "Arial" },
228 { SANS, "Arimo" },
229 { SANS, "Liberation Sans" },
230
231 { SERIF, "Times New Roman" },
232 { SERIF, "Tinos" },
233 { SERIF, "Liberation Serif" },
234
235 { MONO, "Courier New" },
236 { MONO, "Cousine" },
237 { MONO, "Liberation Mono" },
238
239 { SYMBOL, "Symbol" },
240 { SYMBOL, "Symbol Neu" },
241
242 // MS Pゴシック
243 { PGOTHIC, "MS PGothic" },
244 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
245 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshin7476cf52014-09-09 12:30:57 -0700246 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000247 { PGOTHIC, "IPAPGothic" },
248 { PGOTHIC, "MotoyaG04Gothic" },
249
250 // MS ゴシック
251 { GOTHIC, "MS Gothic" },
252 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
253 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshindd4e5682015-05-12 12:08:36 -0700254 { GOTHIC, "Noto Sans Mono CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000255 { GOTHIC, "IPAGothic" },
256 { GOTHIC, "MotoyaG04GothicMono" },
257
258 // MS P明朝
259 { PMINCHO, "MS PMincho" },
260 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
261 "\xe6\x98\x8e\xe6\x9c\x9d"},
262 { PMINCHO, "IPAPMincho" },
263 { PMINCHO, "MotoyaG04Mincho" },
264
265 // MS 明朝
266 { MINCHO, "MS Mincho" },
267 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
268 { MINCHO, "IPAMincho" },
269 { MINCHO, "MotoyaG04MinchoMono" },
270
271 // 宋体
272 { SIMSUN, "Simsun" },
273 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
274 { SIMSUN, "MSung GB18030" },
275 { SIMSUN, "Song ASC" },
276
277 // 新宋体
278 { NSIMSUN, "NSimsun" },
279 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
280 { NSIMSUN, "MSung GB18030" },
281 { NSIMSUN, "N Song ASC" },
282
283 // 黑体
284 { SIMHEI, "Simhei" },
285 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700286 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000287 { SIMHEI, "MYingHeiGB18030" },
288 { SIMHEI, "MYingHeiB5HK" },
289
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000290 // 新細明體
291 { PMINGLIU, "PMingLiU"},
292 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
293 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000294
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000295 // 細明體
296 { MINGLIU, "MingLiU"},
297 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
298 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000299
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000300 // 新細明體
301 { PMINGLIUHK, "PMingLiU_HKSCS"},
302 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
303 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000304
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000305 // 細明體
306 { MINGLIUHK, "MingLiU_HKSCS"},
307 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
308 { MINGLIUHK, "MSung B5HK"},
309
310 // Cambria
311 { CAMBRIA, "Cambria" },
312 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000313
314 // Calibri
315 { CALIBRI, "Calibri" },
316 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000317 };
318
319 static const size_t kFontCount =
320 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
321
322 // TODO(jungshik): If this loop turns out to be hot, turn
323 // the array to a static (hash)map to speed it up.
324 for (size_t i = 0; i < kFontCount; ++i) {
325 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
326 return kFontEquivMap[i].clazz;
327 }
328 return OTHER;
329}
330
331
332// Return true if |font_a| and |font_b| are visually and at the metrics
333// level interchangeable.
334bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
335{
336 FontEquivClass class_a = GetFontEquivClass(font_a);
337 FontEquivClass class_b = GetFontEquivClass(font_b);
338
339 return class_a != OTHER && class_a == class_b;
340}
341
reed@google.comb1c65b62013-02-26 15:50:51 +0000342// Normally we only return exactly the font asked for. In last-resort
343// cases, the request either doesn't specify a font or is one of the
344// basic font names like "Sans", "Serif" or "Monospace". This function
345// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700346bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000347 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700348 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000349 strcasecmp(family_cstr, "sans") == 0 ||
350 strcasecmp(family_cstr, "serif") == 0 ||
351 strcasecmp(family_cstr, "monospace") == 0;
352}
353
reed@google.comb1c65b62013-02-26 15:50:51 +0000354// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
bungeman11a77c62016-04-12 13:45:06 -0700355static int get_int(FcPattern* pattern, const char object[], int missing) {
356 int value;
357 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
358 return missing;
359 }
360 return value;
361}
362
363static int map_range(SkFixed value,
364 SkFixed old_min, SkFixed old_max,
365 SkFixed new_min, SkFixed new_max)
366{
367 SkASSERT(old_min < old_max);
368 SkASSERT(new_min <= new_max);
369 return new_min + SkMulDiv(value - old_min, new_max - new_min, old_max - old_min);
370}
371
372struct MapRanges {
373 SkFixed old_val;
374 SkFixed new_val;
375};
376
377static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int rangesCount) {
378 // -Inf to [0]
379 if (val < ranges[0].old_val) {
380 return ranges[0].new_val;
381 }
382
383 // Linear from [i] to [i+1]
384 for (int i = 0; i < rangesCount - 1; ++i) {
385 if (val < ranges[i+1].old_val) {
386 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
387 ranges[i].new_val, ranges[i+1].new_val);
388 }
389 }
390
391 // From [n] to +Inf
392 // if (fcweight < Inf)
393 return ranges[rangesCount-1].new_val;
394}
395
396static int map_ranges(int val, MapRanges const ranges[], int rangesCount) {
397 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesCount));
398}
399
400template<int n> struct SkTFixed {
401 static_assert(-32768 <= n && n <= 32767, "SkTFixed_n_not_in_range");
402 static const SkFixed value = static_cast<SkFixed>(n << 16);
403};
404
405static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
406 typedef SkFontStyle SkFS;
407
408 static const MapRanges weightRanges[] = {
409 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value },
410 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weight>::value },
411 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value },
412 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::value },
413 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::value },
414 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>::value },
415 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
416 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
417 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
bungeman6e45bda2016-07-25 15:11:49 -0700418 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<SkFS::kExtraBlack_Weight>::value },
bungeman11a77c62016-04-12 13:45:06 -0700419 };
420 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
421 weightRanges, SK_ARRAY_COUNT(weightRanges));
422
423 static const MapRanges widthRanges[] = {
424 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondensed_Width>::value },
425 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondensed_Width>::value },
426 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>::value },
427 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed_Width>::value },
428 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value },
429 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_Width>::value },
430 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::value },
431 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded_Width>::value },
bungemand783e082016-08-01 12:37:13 -0700432 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltraExpanded_Width>::value },
bungeman11a77c62016-04-12 13:45:06 -0700433 };
434 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
435 widthRanges, SK_ARRAY_COUNT(widthRanges));
436
bungemanb4bb7d82016-04-27 10:21:04 -0700437 SkFS::Slant slant = SkFS::kUpright_Slant;
438 switch (get_int(pattern, FC_SLANT, FC_SLANT_ROMAN)) {
439 case FC_SLANT_ROMAN: slant = SkFS::kUpright_Slant; break;
440 case FC_SLANT_ITALIC : slant = SkFS::kItalic_Slant ; break;
441 case FC_SLANT_OBLIQUE: slant = SkFS::kOblique_Slant; break;
442 default: SkASSERT(false); break;
443 }
bungeman11a77c62016-04-12 13:45:06 -0700444
445 return SkFontStyle(weight, width, slant);
446}
447
448static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
449 typedef SkFontStyle SkFS;
450
451 static const MapRanges weightRanges[] = {
452 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value },
453 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIGHT>::value },
454 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value },
455 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::value },
456 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::value },
457 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>::value },
458 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
459 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
460 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
bungeman6e45bda2016-07-25 15:11:49 -0700461 { SkTFixed<SkFS::kExtraBlack_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
bungeman11a77c62016-04-12 13:45:06 -0700462 };
463 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
464
465 static const MapRanges widthRanges[] = {
466 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRACONDENSED>::value },
467 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRACONDENSED>::value },
468 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>::value },
469 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICONDENSED>::value },
470 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value },
471 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPANDED>::value },
472 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::value },
473 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value },
bungemand783e082016-08-01 12:37:13 -0700474 { SkTFixed<SkFS::kUltraExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value },
bungeman11a77c62016-04-12 13:45:06 -0700475 };
476 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges));
477
bungemanb4bb7d82016-04-27 10:21:04 -0700478 int slant = FC_SLANT_ROMAN;
479 switch (style.slant()) {
480 case SkFS::kUpright_Slant: slant = FC_SLANT_ROMAN ; break;
481 case SkFS::kItalic_Slant : slant = FC_SLANT_ITALIC ; break;
482 case SkFS::kOblique_Slant: slant = FC_SLANT_OBLIQUE; break;
483 default: SkASSERT(false); break;
484 }
485
bungeman11a77c62016-04-12 13:45:06 -0700486 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
bungemanb4bb7d82016-04-27 10:21:04 -0700487 FcPatternAddInteger(pattern, FC_WIDTH , width);
488 FcPatternAddInteger(pattern, FC_SLANT , slant);
bungeman11a77c62016-04-12 13:45:06 -0700489}
490
reed@google.comb1c65b62013-02-26 15:50:51 +0000491} // anonymous namespace
492
493///////////////////////////////////////////////////////////////////////////////
494
reed@google.comf71a2332013-02-27 19:06:30 +0000495#define kMaxFontFamilyLength 2048
drott358f93d2016-08-26 10:08:45 -0700496#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
497const char* kFontFormatTrueType = "TrueType";
498const char* kFontFormatCFF = "CFF";
499#endif
reed@google.comb1c65b62013-02-26 15:50:51 +0000500
reed@google.comf71a2332013-02-27 19:06:30 +0000501SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
bungeman02657072016-05-02 11:54:13 -0700502 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000503
reed@google.comb1c65b62013-02-26 15:50:51 +0000504 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000505
506 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000507}
508
509SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
510}
511
benjaminwagner2211a7b2015-12-01 11:12:05 -0800512bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
513 if (access(filename, R_OK) != 0) {
514 return false;
515 }
516 return true;
517}
518
519bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
drott358f93d2016-08-26 10:08:45 -0700520#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
bungeman875b8f62016-09-16 13:19:49 -0700521 const char* font_format = get_string(pattern, FC_FONTFORMAT);
drott358f93d2016-08-26 10:08:45 -0700522 if (font_format
523 && strcmp(font_format, kFontFormatTrueType) != 0
524 && strcmp(font_format, kFontFormatCFF) != 0)
525 {
526 return false;
527 }
528#endif
529
benjaminwagner2211a7b2015-12-01 11:12:05 -0800530 // fontconfig can also return fonts which are unreadable
bungeman875b8f62016-09-16 13:19:49 -0700531 const char* c_filename = get_string(pattern, FC_FILE);
benjaminwagner2211a7b2015-12-01 11:12:05 -0800532 if (!c_filename) {
533 return false;
534 }
535 return this->isAccessible(c_filename);
536}
537
538// Find matching font from |font_set| for the given font family.
539FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
540 const char* post_config_family,
541 const SkString& family) {
542 // Older versions of fontconfig have a bug where they cannot select
543 // only scalable fonts so we have to manually filter the results.
544 FcPattern* match = nullptr;
545 for (int i = 0; i < font_set->nfont; ++i) {
546 FcPattern* current = font_set->fonts[i];
547 if (this->isValidPattern(current)) {
548 match = current;
549 break;
550 }
551 }
552
553 if (match && !IsFallbackFontAllowed(family)) {
554 bool acceptable_substitute = false;
555 for (int id = 0; id < 255; ++id) {
bungeman875b8f62016-09-16 13:19:49 -0700556 const char* post_match_family = get_string(match, FC_FAMILY, id);
benjaminwagner2211a7b2015-12-01 11:12:05 -0800557 if (!post_match_family)
558 break;
559 acceptable_substitute =
560 (strcasecmp(post_config_family, post_match_family) == 0 ||
561 // Workaround for Issue 12530:
562 // requested family: "Bitstream Vera Sans"
563 // post_config_family: "Arial"
564 // post_match_family: "Bitstream Vera Sans"
565 // -> We should treat this case as a good match.
566 strcasecmp(family.c_str(), post_match_family) == 0) ||
567 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
568 if (acceptable_substitute)
569 break;
570 }
571 if (!acceptable_substitute)
572 return nullptr;
573 }
574
575 return match;
576}
577
bungeman11a77c62016-04-12 13:45:06 -0700578bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
579 SkFontStyle style,
580 FontIdentity* outIdentity,
581 SkString* outFamilyName,
582 SkFontStyle* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700583 SkString familyStr(familyName ? familyName : "");
584 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000585 return false;
586 }
587
bungeman02657072016-05-02 11:54:13 -0700588 FCLocker lock;
reed@google.comb1c65b62013-02-26 15:50:51 +0000589
reed@google.comb1c65b62013-02-26 15:50:51 +0000590 FcPattern* pattern = FcPatternCreate();
591
reed@google.comee619a02013-02-26 22:58:09 +0000592 if (familyName) {
593 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
594 }
bungeman11a77c62016-04-12 13:45:06 -0700595 fcpattern_from_skfontstyle(style, pattern);
bungeman11a77c62016-04-12 13:45:06 -0700596
reed@google.comb1c65b62013-02-26 15:50:51 +0000597 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
598
halcanary96fcdcc2015-08-27 07:41:13 -0700599 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000600 FcDefaultSubstitute(pattern);
601
602 // Font matching:
603 // CSS often specifies a fallback list of families:
604 // font-family: a, b, c, serif;
605 // However, fontconfig will always do its best to find *a* font when asked
606 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700607 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000608 // and lets WebKit know to try the next CSS family name. However, fontconfig
609 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
610 // wish to support that.
611 //
612 // Thus, if a specific family is requested we set @family_requested. Then we
613 // record two strings: the family name after config processing and the
614 // family name after resolving. If the two are equal, it's a good match.
615 //
616 // So consider the case where a user has mapped Arial to Helvetica in their
617 // config.
618 // requested family: "Arial"
619 // post_config_family: "Helvetica"
620 // post_match_family: "Helvetica"
621 // -> good match
622 //
623 // and for a missing font:
624 // requested family: "Monaco"
625 // post_config_family: "Monaco"
626 // post_match_family: "Times New Roman"
627 // -> BAD match
628 //
629 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000630
bungeman875b8f62016-09-16 13:19:49 -0700631 const char* post_config_family = get_string(pattern, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000632 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000633 // we can just continue with an empty name, e.g. default font
634 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000635 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000636
637 FcResult result;
bungeman6d195b22016-05-24 08:08:20 -0700638 FcFontSet* font_set = FcFontSort(nullptr, pattern, 0, nullptr, &result);
reed@google.comb1c65b62013-02-26 15:50:51 +0000639 if (!font_set) {
640 FcPatternDestroy(pattern);
641 return false;
642 }
643
benjaminwagner2211a7b2015-12-01 11:12:05 -0800644 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000645 if (!match) {
646 FcPatternDestroy(pattern);
647 FcFontSetDestroy(font_set);
648 return false;
649 }
650
651 FcPatternDestroy(pattern);
652
reed@google.comf71a2332013-02-27 19:06:30 +0000653 // From here out we just extract our results from 'match'
654
bungeman875b8f62016-09-16 13:19:49 -0700655 post_config_family = get_string(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000656 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000657 FcFontSetDestroy(font_set);
658 return false;
659 }
660
bungeman875b8f62016-09-16 13:19:49 -0700661 const char* c_filename = get_string(match, FC_FILE);
reed@google.come49d67e2013-04-22 18:00:06 +0000662 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000663 FcFontSetDestroy(font_set);
664 return false;
665 }
reed@google.comf71a2332013-02-27 19:06:30 +0000666
bungeman875b8f62016-09-16 13:19:49 -0700667 int face_index = get_int(match, FC_INDEX, 0);
reed@google.comb1c65b62013-02-26 15:50:51 +0000668
reed@google.comb1c65b62013-02-26 15:50:51 +0000669 FcFontSetDestroy(font_set);
670
reed@google.comf71a2332013-02-27 19:06:30 +0000671 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000672 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000673 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000674 }
reed@google.comf71a2332013-02-27 19:06:30 +0000675 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000676 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000677 }
reed@google.comf71a2332013-02-27 19:06:30 +0000678 if (outStyle) {
bungemaned2edab2016-04-13 05:23:35 -0700679 *outStyle = skfontstyle_from_fcpattern(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000680 }
reed@google.comee619a02013-02-26 22:58:09 +0000681 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000682}
683
bungeman5f213d92015-01-27 05:39:10 -0800684SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
bungemanf93d7112016-09-16 06:24:20 -0700685 return SkStream::MakeFromFile(identity.fString.c_str()).release();
reed@google.comb1c65b62013-02-26 15:50:51 +0000686}