blob: cdd420c38c675b879683c5cd5227e06fe2b36766 [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
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 },
bungeman6e45bda2016-07-25 15:11:49 -0700421 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<SkFS::kExtraBlack_Weight>::value },
bungeman11a77c62016-04-12 13:45:06 -0700422 };
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 },
bungemand783e082016-08-01 12:37:13 -0700435 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltraExpanded_Width>::value },
bungeman11a77c62016-04-12 13:45:06 -0700436 };
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 },
bungeman6e45bda2016-07-25 15:11:49 -0700464 { SkTFixed<SkFS::kExtraBlack_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
bungeman11a77c62016-04-12 13:45:06 -0700465 };
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 },
bungemand783e082016-08-01 12:37:13 -0700477 { SkTFixed<SkFS::kUltraExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value },
bungeman11a77c62016-04-12 13:45:06 -0700478 };
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
drott358f93d2016-08-26 10:08:45 -0700499#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
500const char* kFontFormatTrueType = "TrueType";
501const char* kFontFormatCFF = "CFF";
502#endif
reed@google.comb1c65b62013-02-26 15:50:51 +0000503
reed@google.comf71a2332013-02-27 19:06:30 +0000504SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
bungeman02657072016-05-02 11:54:13 -0700505 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000506
reed@google.comb1c65b62013-02-26 15:50:51 +0000507 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000508
509 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000510}
511
512SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
513}
514
benjaminwagner2211a7b2015-12-01 11:12:05 -0800515bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
516 if (access(filename, R_OK) != 0) {
517 return false;
518 }
519 return true;
520}
521
522bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
523#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
524 FcBool is_scalable;
525 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
526 || !is_scalable) {
527 return false;
528 }
529#endif
530
drott358f93d2016-08-26 10:08:45 -0700531#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
532 const char* font_format = get_name(pattern, FC_FONTFORMAT);
533 if (font_format
534 && strcmp(font_format, kFontFormatTrueType) != 0
535 && strcmp(font_format, kFontFormatCFF) != 0)
536 {
537 return false;
538 }
539#endif
540
benjaminwagner2211a7b2015-12-01 11:12:05 -0800541 // fontconfig can also return fonts which are unreadable
542 const char* c_filename = get_name(pattern, FC_FILE);
543 if (!c_filename) {
544 return false;
545 }
546 return this->isAccessible(c_filename);
547}
548
549// Find matching font from |font_set| for the given font family.
550FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
551 const char* post_config_family,
552 const SkString& family) {
553 // Older versions of fontconfig have a bug where they cannot select
554 // only scalable fonts so we have to manually filter the results.
555 FcPattern* match = nullptr;
556 for (int i = 0; i < font_set->nfont; ++i) {
557 FcPattern* current = font_set->fonts[i];
558 if (this->isValidPattern(current)) {
559 match = current;
560 break;
561 }
562 }
563
564 if (match && !IsFallbackFontAllowed(family)) {
565 bool acceptable_substitute = false;
566 for (int id = 0; id < 255; ++id) {
567 const char* post_match_family = get_name(match, FC_FAMILY, id);
568 if (!post_match_family)
569 break;
570 acceptable_substitute =
571 (strcasecmp(post_config_family, post_match_family) == 0 ||
572 // Workaround for Issue 12530:
573 // requested family: "Bitstream Vera Sans"
574 // post_config_family: "Arial"
575 // post_match_family: "Bitstream Vera Sans"
576 // -> We should treat this case as a good match.
577 strcasecmp(family.c_str(), post_match_family) == 0) ||
578 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
579 if (acceptable_substitute)
580 break;
581 }
582 if (!acceptable_substitute)
583 return nullptr;
584 }
585
586 return match;
587}
588
bungeman11a77c62016-04-12 13:45:06 -0700589bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
590 SkFontStyle style,
591 FontIdentity* outIdentity,
592 SkString* outFamilyName,
593 SkFontStyle* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700594 SkString familyStr(familyName ? familyName : "");
595 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000596 return false;
597 }
598
bungeman02657072016-05-02 11:54:13 -0700599 FCLocker lock;
reed@google.comb1c65b62013-02-26 15:50:51 +0000600
reed@google.comb1c65b62013-02-26 15:50:51 +0000601 FcPattern* pattern = FcPatternCreate();
602
reed@google.comee619a02013-02-26 22:58:09 +0000603 if (familyName) {
604 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
605 }
bungeman11a77c62016-04-12 13:45:06 -0700606 fcpattern_from_skfontstyle(style, pattern);
bungeman11a77c62016-04-12 13:45:06 -0700607
reed@google.comb1c65b62013-02-26 15:50:51 +0000608 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
609
drott358f93d2016-08-26 10:08:45 -0700610#ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
611 FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8*>(kFontFormatTrueType));
612 FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8*>(kFontFormatCFF));
613#endif
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
647 const char* post_config_family = get_name(pattern, FC_FAMILY);
648 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
reed@google.come49d67e2013-04-22 18:00:06 +0000671 post_config_family = get_name(match, FC_FAMILY);
672 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000673 FcFontSetDestroy(font_set);
674 return false;
675 }
676
reed@google.come49d67e2013-04-22 18:00:06 +0000677 const char* c_filename = get_name(match, FC_FILE);
678 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
reed@google.comb1c65b62013-02-26 15:50:51 +0000683 int face_index;
684 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
685 FcFontSetDestroy(font_set);
686 return false;
687 }
688
reed@google.comb1c65b62013-02-26 15:50:51 +0000689 FcFontSetDestroy(font_set);
690
reed@google.comf71a2332013-02-27 19:06:30 +0000691 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000692 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000693 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000694 }
reed@google.comf71a2332013-02-27 19:06:30 +0000695 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000696 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000697 }
reed@google.comf71a2332013-02-27 19:06:30 +0000698 if (outStyle) {
bungemaned2edab2016-04-13 05:23:35 -0700699 *outStyle = skfontstyle_from_fcpattern(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000700 }
reed@google.comee619a02013-02-26 22:58:09 +0000701 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000702}
703
bungeman5f213d92015-01-27 05:39:10 -0800704SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
bungemanf93d7112016-09-16 06:24:20 -0700705 return SkStream::MakeFromFile(identity.fString.c_str()).release();
reed@google.comb1c65b62013-02-26 15:50:51 +0000706}
reed@google.com027fd202013-04-19 20:45:30 +0000707
708///////////////////////////////////////////////////////////////////////////////
709
reed@google.com027fd202013-04-19 20:45:30 +0000710static bool find_name(const SkTDArray<const char*>& list, const char* str) {
711 int count = list.count();
712 for (int i = 0; i < count; ++i) {
713 if (!strcmp(list[i], str)) {
714 return true;
715 }
716 }
717 return false;
718}
719
bungemanfeb3c1a2016-08-05 06:51:50 -0700720sk_sp<SkDataTable> SkFontConfigInterfaceDirect::getFamilyNames() {
bungeman02657072016-05-02 11:54:13 -0700721 FCLocker lock;
reed@google.comf55061f2013-04-22 18:48:45 +0000722
reed@google.com027fd202013-04-19 20:45:30 +0000723 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000724 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
halcanary96fcdcc2015-08-27 07:41:13 -0700725 if (nullptr == pat) {
726 return nullptr;
bungeman@google.com02dd6882013-07-22 14:10:08 +0000727 }
728
729 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
730 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
halcanary96fcdcc2015-08-27 07:41:13 -0700731 if (nullptr == os) {
732 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000733 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000734
halcanary96fcdcc2015-08-27 07:41:13 -0700735 FcFontSet* fs = FcFontList(nullptr, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000736 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
halcanary96fcdcc2015-08-27 07:41:13 -0700737 if (nullptr == fs) {
738 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000739 }
740
741 SkTDArray<const char*> names;
742 SkTDArray<size_t> sizes;
743 for (int i = 0; i < fs->nfont; ++i) {
744 FcPattern* match = fs->fonts[i];
745 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000746 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000747 *names.append() = famName;
748 *sizes.append() = strlen(famName) + 1;
749 }
750 }
751
bungemanfeb3c1a2016-08-05 06:51:50 -0700752 return SkDataTable::MakeCopyArrays((const void*const*)names.begin(),
753 sizes.begin(), names.count());
reed@google.com027fd202013-04-19 20:45:30 +0000754}