blob: 875000daa53c2d805ede59a8273e4faefcf4c792 [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"},
Jungshik Shincae60da2017-07-18 16:42:32 -0700262 { PMINCHO, "Noto Serif CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000263 { 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" },
Jungshik Shincae60da2017-07-18 16:42:32 -0700269 { MINCHO, "Noto Serif CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000270 { MINCHO, "IPAMincho" },
271 { MINCHO, "MotoyaG04MinchoMono" },
272
273 // 宋体
274 { SIMSUN, "Simsun" },
275 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
Jungshik Shincae60da2017-07-18 16:42:32 -0700276 { SIMSUN, "Noto Serif CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000277 { SIMSUN, "MSung GB18030" },
278 { SIMSUN, "Song ASC" },
279
280 // 新宋体
281 { NSIMSUN, "NSimsun" },
282 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
Jungshik Shincae60da2017-07-18 16:42:32 -0700283 { NSIMSUN, "Noto Serif CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000284 { NSIMSUN, "MSung GB18030" },
285 { NSIMSUN, "N Song ASC" },
286
287 // 黑体
288 { SIMHEI, "Simhei" },
289 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700290 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000291 { SIMHEI, "MYingHeiGB18030" },
292 { SIMHEI, "MYingHeiB5HK" },
293
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000294 // 新細明體
295 { PMINGLIU, "PMingLiU"},
296 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
Jungshik Shincae60da2017-07-18 16:42:32 -0700297 { PMINGLIU, "Noto Serif CJK TC"},
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000298 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000299
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000300 // 細明體
301 { MINGLIU, "MingLiU"},
302 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
Jungshik Shincae60da2017-07-18 16:42:32 -0700303 { MINGLIU, "Noto Serif CJK TC"},
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000304 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000305
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000306 // 新細明體
307 { PMINGLIUHK, "PMingLiU_HKSCS"},
308 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
Jungshik Shincae60da2017-07-18 16:42:32 -0700309 { PMINGLIUHK, "Noto Serif CJK TC"},
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000310 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000311
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000312 // 細明體
313 { MINGLIUHK, "MingLiU_HKSCS"},
314 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
Jungshik Shincae60da2017-07-18 16:42:32 -0700315 { MINGLIUHK, "Noto Serif CJK TC"},
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000316 { MINGLIUHK, "MSung B5HK"},
317
318 // Cambria
319 { CAMBRIA, "Cambria" },
320 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000321
322 // Calibri
323 { CALIBRI, "Calibri" },
324 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000325 };
326
327 static const size_t kFontCount =
328 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
329
330 // TODO(jungshik): If this loop turns out to be hot, turn
331 // the array to a static (hash)map to speed it up.
332 for (size_t i = 0; i < kFontCount; ++i) {
333 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
334 return kFontEquivMap[i].clazz;
335 }
336 return OTHER;
337}
338
339
340// Return true if |font_a| and |font_b| are visually and at the metrics
341// level interchangeable.
342bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
343{
344 FontEquivClass class_a = GetFontEquivClass(font_a);
345 FontEquivClass class_b = GetFontEquivClass(font_b);
346
347 return class_a != OTHER && class_a == class_b;
348}
349
reed@google.comb1c65b62013-02-26 15:50:51 +0000350// Normally we only return exactly the font asked for. In last-resort
351// cases, the request either doesn't specify a font or is one of the
352// basic font names like "Sans", "Serif" or "Monospace". This function
353// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700354bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000355 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700356 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000357 strcasecmp(family_cstr, "sans") == 0 ||
358 strcasecmp(family_cstr, "serif") == 0 ||
359 strcasecmp(family_cstr, "monospace") == 0;
360}
361
reed@google.comb1c65b62013-02-26 15:50:51 +0000362// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
bungeman11a77c62016-04-12 13:45:06 -0700363static int get_int(FcPattern* pattern, const char object[], int missing) {
364 int value;
365 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
366 return missing;
367 }
368 return value;
369}
370
371static int map_range(SkFixed value,
372 SkFixed old_min, SkFixed old_max,
373 SkFixed new_min, SkFixed new_max)
374{
375 SkASSERT(old_min < old_max);
376 SkASSERT(new_min <= new_max);
377 return new_min + SkMulDiv(value - old_min, new_max - new_min, old_max - old_min);
378}
379
380struct MapRanges {
381 SkFixed old_val;
382 SkFixed new_val;
383};
384
385static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int rangesCount) {
386 // -Inf to [0]
387 if (val < ranges[0].old_val) {
388 return ranges[0].new_val;
389 }
390
391 // Linear from [i] to [i+1]
392 for (int i = 0; i < rangesCount - 1; ++i) {
393 if (val < ranges[i+1].old_val) {
394 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
395 ranges[i].new_val, ranges[i+1].new_val);
396 }
397 }
398
399 // From [n] to +Inf
400 // if (fcweight < Inf)
401 return ranges[rangesCount-1].new_val;
402}
403
404static int map_ranges(int val, MapRanges const ranges[], int rangesCount) {
405 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesCount));
406}
407
408template<int n> struct SkTFixed {
409 static_assert(-32768 <= n && n <= 32767, "SkTFixed_n_not_in_range");
410 static const SkFixed value = static_cast<SkFixed>(n << 16);
411};
412
Ben Wagner13197b82017-08-14 11:06:12 -0400413#ifndef FC_WEIGHT_DEMILIGHT
414#define FC_WEIGHT_DEMILIGHT 65
415#endif
416
bungeman11a77c62016-04-12 13:45:06 -0700417static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
418 typedef SkFontStyle SkFS;
419
420 static const MapRanges weightRanges[] = {
421 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value },
422 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weight>::value },
423 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value },
Ben Wagner13197b82017-08-14 11:06:12 -0400424 { SkTFixed<FC_WEIGHT_DEMILIGHT>::value, SkTFixed<350>::value },
425 { SkTFixed<FC_WEIGHT_BOOK>::value, SkTFixed<380>::value },
bungeman11a77c62016-04-12 13:45:06 -0700426 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::value },
427 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::value },
428 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>::value },
429 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
430 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
431 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
bungeman6e45bda2016-07-25 15:11:49 -0700432 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<SkFS::kExtraBlack_Weight>::value },
bungeman11a77c62016-04-12 13:45:06 -0700433 };
434 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
435 weightRanges, SK_ARRAY_COUNT(weightRanges));
436
437 static const MapRanges widthRanges[] = {
438 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondensed_Width>::value },
439 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondensed_Width>::value },
440 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>::value },
441 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed_Width>::value },
442 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value },
443 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_Width>::value },
444 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::value },
445 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded_Width>::value },
bungemand783e082016-08-01 12:37:13 -0700446 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltraExpanded_Width>::value },
bungeman11a77c62016-04-12 13:45:06 -0700447 };
448 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
449 widthRanges, SK_ARRAY_COUNT(widthRanges));
450
bungemanb4bb7d82016-04-27 10:21:04 -0700451 SkFS::Slant slant = SkFS::kUpright_Slant;
452 switch (get_int(pattern, FC_SLANT, FC_SLANT_ROMAN)) {
453 case FC_SLANT_ROMAN: slant = SkFS::kUpright_Slant; break;
454 case FC_SLANT_ITALIC : slant = SkFS::kItalic_Slant ; break;
455 case FC_SLANT_OBLIQUE: slant = SkFS::kOblique_Slant; break;
456 default: SkASSERT(false); break;
457 }
bungeman11a77c62016-04-12 13:45:06 -0700458
459 return SkFontStyle(weight, width, slant);
460}
461
462static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
463 typedef SkFontStyle SkFS;
464
465 static const MapRanges weightRanges[] = {
466 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value },
467 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIGHT>::value },
468 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value },
Ben Wagner13197b82017-08-14 11:06:12 -0400469 { SkTFixed<350>::value, SkTFixed<FC_WEIGHT_DEMILIGHT>::value },
470 { SkTFixed<380>::value, SkTFixed<FC_WEIGHT_BOOK>::value },
bungeman11a77c62016-04-12 13:45:06 -0700471 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::value },
472 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::value },
473 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>::value },
474 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
475 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
476 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
bungeman6e45bda2016-07-25 15:11:49 -0700477 { SkTFixed<SkFS::kExtraBlack_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
bungeman11a77c62016-04-12 13:45:06 -0700478 };
479 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
480
481 static const MapRanges widthRanges[] = {
482 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRACONDENSED>::value },
483 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRACONDENSED>::value },
484 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>::value },
485 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICONDENSED>::value },
486 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value },
487 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPANDED>::value },
488 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::value },
489 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value },
bungemand783e082016-08-01 12:37:13 -0700490 { SkTFixed<SkFS::kUltraExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value },
bungeman11a77c62016-04-12 13:45:06 -0700491 };
492 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges));
493
bungemanb4bb7d82016-04-27 10:21:04 -0700494 int slant = FC_SLANT_ROMAN;
495 switch (style.slant()) {
496 case SkFS::kUpright_Slant: slant = FC_SLANT_ROMAN ; break;
497 case SkFS::kItalic_Slant : slant = FC_SLANT_ITALIC ; break;
498 case SkFS::kOblique_Slant: slant = FC_SLANT_OBLIQUE; break;
499 default: SkASSERT(false); break;
500 }
501
bungeman11a77c62016-04-12 13:45:06 -0700502 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
bungemanb4bb7d82016-04-27 10:21:04 -0700503 FcPatternAddInteger(pattern, FC_WIDTH , width);
504 FcPatternAddInteger(pattern, FC_SLANT , slant);
bungeman11a77c62016-04-12 13:45:06 -0700505}
506
reed@google.comb1c65b62013-02-26 15:50:51 +0000507} // anonymous namespace
508
509///////////////////////////////////////////////////////////////////////////////
510
reed@google.comf71a2332013-02-27 19:06:30 +0000511#define kMaxFontFamilyLength 2048
drott358f93d2016-08-26 10:08:45 -0700512#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
513const char* kFontFormatTrueType = "TrueType";
514const char* kFontFormatCFF = "CFF";
515#endif
reed@google.comb1c65b62013-02-26 15:50:51 +0000516
reed@google.comf71a2332013-02-27 19:06:30 +0000517SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
bungeman02657072016-05-02 11:54:13 -0700518 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000519
reed@google.comb1c65b62013-02-26 15:50:51 +0000520 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000521
522 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000523}
524
525SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
526}
527
benjaminwagner2211a7b2015-12-01 11:12:05 -0800528bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
529 if (access(filename, R_OK) != 0) {
530 return false;
531 }
532 return true;
533}
534
535bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
drott358f93d2016-08-26 10:08:45 -0700536#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
bungeman875b8f62016-09-16 13:19:49 -0700537 const char* font_format = get_string(pattern, FC_FONTFORMAT);
drott358f93d2016-08-26 10:08:45 -0700538 if (font_format
539 && strcmp(font_format, kFontFormatTrueType) != 0
540 && strcmp(font_format, kFontFormatCFF) != 0)
541 {
542 return false;
543 }
544#endif
545
benjaminwagner2211a7b2015-12-01 11:12:05 -0800546 // fontconfig can also return fonts which are unreadable
bungeman875b8f62016-09-16 13:19:49 -0700547 const char* c_filename = get_string(pattern, FC_FILE);
benjaminwagner2211a7b2015-12-01 11:12:05 -0800548 if (!c_filename) {
549 return false;
550 }
551 return this->isAccessible(c_filename);
552}
553
554// Find matching font from |font_set| for the given font family.
555FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
556 const char* post_config_family,
557 const SkString& family) {
558 // Older versions of fontconfig have a bug where they cannot select
559 // only scalable fonts so we have to manually filter the results.
560 FcPattern* match = nullptr;
561 for (int i = 0; i < font_set->nfont; ++i) {
562 FcPattern* current = font_set->fonts[i];
563 if (this->isValidPattern(current)) {
564 match = current;
565 break;
566 }
567 }
568
569 if (match && !IsFallbackFontAllowed(family)) {
570 bool acceptable_substitute = false;
571 for (int id = 0; id < 255; ++id) {
bungeman875b8f62016-09-16 13:19:49 -0700572 const char* post_match_family = get_string(match, FC_FAMILY, id);
benjaminwagner2211a7b2015-12-01 11:12:05 -0800573 if (!post_match_family)
574 break;
575 acceptable_substitute =
576 (strcasecmp(post_config_family, post_match_family) == 0 ||
577 // Workaround for Issue 12530:
578 // requested family: "Bitstream Vera Sans"
579 // post_config_family: "Arial"
580 // post_match_family: "Bitstream Vera Sans"
581 // -> We should treat this case as a good match.
582 strcasecmp(family.c_str(), post_match_family) == 0) ||
583 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
584 if (acceptable_substitute)
585 break;
586 }
587 if (!acceptable_substitute)
588 return nullptr;
589 }
590
591 return match;
592}
593
bungeman11a77c62016-04-12 13:45:06 -0700594bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
595 SkFontStyle style,
596 FontIdentity* outIdentity,
597 SkString* outFamilyName,
598 SkFontStyle* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700599 SkString familyStr(familyName ? familyName : "");
600 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000601 return false;
602 }
603
bungeman02657072016-05-02 11:54:13 -0700604 FCLocker lock;
reed@google.comb1c65b62013-02-26 15:50:51 +0000605
reed@google.comb1c65b62013-02-26 15:50:51 +0000606 FcPattern* pattern = FcPatternCreate();
607
reed@google.comee619a02013-02-26 22:58:09 +0000608 if (familyName) {
609 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
610 }
bungeman11a77c62016-04-12 13:45:06 -0700611 fcpattern_from_skfontstyle(style, pattern);
bungeman11a77c62016-04-12 13:45:06 -0700612
reed@google.comb1c65b62013-02-26 15:50:51 +0000613 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
614
halcanary96fcdcc2015-08-27 07:41:13 -0700615 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000616 FcDefaultSubstitute(pattern);
617
618 // Font matching:
619 // CSS often specifies a fallback list of families:
620 // font-family: a, b, c, serif;
621 // However, fontconfig will always do its best to find *a* font when asked
622 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700623 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000624 // and lets WebKit know to try the next CSS family name. However, fontconfig
625 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
626 // wish to support that.
627 //
628 // Thus, if a specific family is requested we set @family_requested. Then we
629 // record two strings: the family name after config processing and the
630 // family name after resolving. If the two are equal, it's a good match.
631 //
632 // So consider the case where a user has mapped Arial to Helvetica in their
633 // config.
634 // requested family: "Arial"
635 // post_config_family: "Helvetica"
636 // post_match_family: "Helvetica"
637 // -> good match
638 //
639 // and for a missing font:
640 // requested family: "Monaco"
641 // post_config_family: "Monaco"
642 // post_match_family: "Times New Roman"
643 // -> BAD match
644 //
645 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000646
bungeman875b8f62016-09-16 13:19:49 -0700647 const char* post_config_family = get_string(pattern, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000648 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000649 // we can just continue with an empty name, e.g. default font
650 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000651 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000652
653 FcResult result;
bungeman6d195b22016-05-24 08:08:20 -0700654 FcFontSet* font_set = FcFontSort(nullptr, pattern, 0, nullptr, &result);
reed@google.comb1c65b62013-02-26 15:50:51 +0000655 if (!font_set) {
656 FcPatternDestroy(pattern);
657 return false;
658 }
659
benjaminwagner2211a7b2015-12-01 11:12:05 -0800660 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000661 if (!match) {
662 FcPatternDestroy(pattern);
663 FcFontSetDestroy(font_set);
664 return false;
665 }
666
667 FcPatternDestroy(pattern);
668
reed@google.comf71a2332013-02-27 19:06:30 +0000669 // From here out we just extract our results from 'match'
670
bungeman875b8f62016-09-16 13:19:49 -0700671 post_config_family = get_string(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000672 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000673 FcFontSetDestroy(font_set);
674 return false;
675 }
676
bungeman875b8f62016-09-16 13:19:49 -0700677 const char* c_filename = get_string(match, FC_FILE);
reed@google.come49d67e2013-04-22 18:00:06 +0000678 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000679 FcFontSetDestroy(font_set);
680 return false;
681 }
reed@google.comf71a2332013-02-27 19:06:30 +0000682
bungeman875b8f62016-09-16 13:19:49 -0700683 int face_index = get_int(match, FC_INDEX, 0);
reed@google.comb1c65b62013-02-26 15:50:51 +0000684
reed@google.comb1c65b62013-02-26 15:50:51 +0000685 FcFontSetDestroy(font_set);
686
reed@google.comf71a2332013-02-27 19:06:30 +0000687 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000688 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000689 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000690 }
reed@google.comf71a2332013-02-27 19:06:30 +0000691 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000692 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000693 }
reed@google.comf71a2332013-02-27 19:06:30 +0000694 if (outStyle) {
bungemaned2edab2016-04-13 05:23:35 -0700695 *outStyle = skfontstyle_from_fcpattern(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000696 }
reed@google.comee619a02013-02-26 22:58:09 +0000697 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000698}
699
bungeman5f213d92015-01-27 05:39:10 -0800700SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
bungemanf93d7112016-09-16 06:24:20 -0700701 return SkStream::MakeFromFile(identity.fString.c_str()).release();
reed@google.comb1c65b62013-02-26 15:50:51 +0000702}