blob: 1c42e2d37e7585678c8e28be47dcb56980a67d60 [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
reed@google.comf55061f2013-04-22 18:48:45 +000027size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
28 size_t size = sizeof(fID) + sizeof(fTTCIndex);
29 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
30 size += sizeof(int32_t) + fString.size(); // store length+data
31 if (addr) {
32 SkWBuffer buffer(addr, size);
33
34 buffer.write32(fID);
35 buffer.write32(fTTCIndex);
36 buffer.write32(fString.size());
37 buffer.write32(fStyle.weight());
38 buffer.write32(fStyle.width());
39 buffer.write8(fStyle.slant());
40 buffer.write(fString.c_str(), fString.size());
41 buffer.padToAlign4();
42
43 SkASSERT(buffer.pos() == size);
44 }
45 return size;
46}
47
48size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
49 size_t size) {
50 SkRBuffer buffer(addr, size);
51
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000052 (void)buffer.readU32(&fID);
53 (void)buffer.readS32(&fTTCIndex);
54 uint32_t strLen, weight, width;
55 (void)buffer.readU32(&strLen);
56 (void)buffer.readU32(&weight);
57 (void)buffer.readU32(&width);
58 uint8_t u8;
59 (void)buffer.readU8(&u8);
60 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
reed@google.comf55061f2013-04-22 18:48:45 +000061 fStyle = SkFontStyle(weight, width, slant);
62 fString.resize(strLen);
commit-bot@chromium.org8f457e32013-11-08 19:22:57 +000063 (void)buffer.read(fString.writable_str(), strLen);
reed@google.comf55061f2013-04-22 18:48:45 +000064 buffer.skipToAlign4();
65
66 return buffer.pos(); // the actual number of bytes read
67}
68
69#ifdef SK_DEBUG
70static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
71 iden->fID = 10;
72 iden->fTTCIndex = 2;
73 iden->fString.set("Hello world");
74 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
75}
76
77static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
78 int initValue) {
79 SkFontConfigInterface::FontIdentity iden1;
80
halcanary96fcdcc2015-08-27 07:41:13 -070081 size_t size0 = iden0.writeToMemory(nullptr);
reed@google.comf55061f2013-04-22 18:48:45 +000082
83 SkAutoMalloc storage(size0);
84 memset(storage.get(), initValue, size0);
85
86 size_t size1 = iden0.writeToMemory(storage.get());
87 SkASSERT(size0 == size1);
88
89 SkASSERT(iden0 != iden1);
90 size_t size2 = iden1.readFromMemory(storage.get(), size1);
91 SkASSERT(size2 == size1);
92 SkASSERT(iden0 == iden1);
93}
94
95static void fontconfiginterface_unittest() {
96 SkFontConfigInterface::FontIdentity iden0, iden1;
97
98 SkASSERT(iden0 == iden1);
99
100 make_iden(&iden0);
101 SkASSERT(iden0 != iden1);
102
103 make_iden(&iden1);
104 SkASSERT(iden0 == iden1);
105
106 test_writeToMemory(iden0, 0);
107 test_writeToMemory(iden0, 0);
108}
109#endif
110
reed@google.com54c69142013-04-09 15:54:52 +0000111///////////////////////////////////////////////////////////////////////////////
112
halcanary96fcdcc2015-08-27 07:41:13 -0700113// Returns the string from the pattern, or nullptr
reed@google.come49d67e2013-04-22 18:00:06 +0000114static const char* get_name(FcPattern* pattern, const char field[],
115 int index = 0) {
116 const char* name;
117 if (FcPatternGetString(pattern, field, index,
118 (FcChar8**)&name) != FcResultMatch) {
halcanary96fcdcc2015-08-27 07:41:13 -0700119 name = nullptr;
reed@google.come49d67e2013-04-22 18:00:06 +0000120 }
121 return name;
122}
123
124///////////////////////////////////////////////////////////////////////////////
125
reed@google.comb1c65b62013-02-26 15:50:51 +0000126namespace {
127
128// Equivalence classes, used to match the Liberation and other fonts
129// with their metric-compatible replacements. See the discussion in
130// GetFontEquivClass().
131enum FontEquivClass
132{
133 OTHER,
134 SANS,
135 SERIF,
136 MONO,
137 SYMBOL,
138 PGOTHIC,
139 GOTHIC,
140 PMINCHO,
141 MINCHO,
142 SIMSUN,
143 NSIMSUN,
144 SIMHEI,
145 PMINGLIU,
146 MINGLIU,
147 PMINGLIUHK,
148 MINGLIUHK,
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000149 CAMBRIA,
bungeman@google.comc526c712013-09-20 17:41:52 +0000150 CALIBRI,
reed@google.comb1c65b62013-02-26 15:50:51 +0000151};
152
153// Match the font name against a whilelist of fonts, returning the equivalence
154// class.
155FontEquivClass GetFontEquivClass(const char* fontname)
156{
157 // It would be nice for fontconfig to tell us whether a given suggested
158 // replacement is a "strong" match (that is, an equivalent font) or
159 // a "weak" match (that is, fontconfig's next-best attempt at finding a
160 // substitute). However, I played around with the fontconfig API for
161 // a good few hours and could not make it reveal this information.
162 //
163 // So instead, we hardcode. Initially this function emulated
164 // /etc/fonts/conf.d/30-metric-aliases.conf
165 // from my Ubuntu system, but we're better off being very conservative.
166
167 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
168 // Arial, Times New Roman and Courier New with a character repertoire
169 // much larger than Liberation. Note that Cousine is metrically
170 // compatible with Courier New, but the former is sans-serif while
171 // the latter is serif.
172
173
174 struct FontEquivMap {
175 FontEquivClass clazz;
176 const char name[40];
177 };
178
179 static const FontEquivMap kFontEquivMap[] = {
180 { SANS, "Arial" },
181 { SANS, "Arimo" },
182 { SANS, "Liberation Sans" },
183
184 { SERIF, "Times New Roman" },
185 { SERIF, "Tinos" },
186 { SERIF, "Liberation Serif" },
187
188 { MONO, "Courier New" },
189 { MONO, "Cousine" },
190 { MONO, "Liberation Mono" },
191
192 { SYMBOL, "Symbol" },
193 { SYMBOL, "Symbol Neu" },
194
195 // MS Pゴシック
196 { PGOTHIC, "MS PGothic" },
197 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
198 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshin7476cf52014-09-09 12:30:57 -0700199 { PGOTHIC, "Noto Sans CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000200 { PGOTHIC, "IPAPGothic" },
201 { PGOTHIC, "MotoyaG04Gothic" },
202
203 // MS ゴシック
204 { GOTHIC, "MS Gothic" },
205 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
206 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
jshindd4e5682015-05-12 12:08:36 -0700207 { GOTHIC, "Noto Sans Mono CJK JP" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000208 { GOTHIC, "IPAGothic" },
209 { GOTHIC, "MotoyaG04GothicMono" },
210
211 // MS P明朝
212 { PMINCHO, "MS PMincho" },
213 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
214 "\xe6\x98\x8e\xe6\x9c\x9d"},
215 { PMINCHO, "IPAPMincho" },
216 { PMINCHO, "MotoyaG04Mincho" },
217
218 // MS 明朝
219 { MINCHO, "MS Mincho" },
220 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
221 { MINCHO, "IPAMincho" },
222 { MINCHO, "MotoyaG04MinchoMono" },
223
224 // 宋体
225 { SIMSUN, "Simsun" },
226 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
227 { SIMSUN, "MSung GB18030" },
228 { SIMSUN, "Song ASC" },
229
230 // 新宋体
231 { NSIMSUN, "NSimsun" },
232 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
233 { NSIMSUN, "MSung GB18030" },
234 { NSIMSUN, "N Song ASC" },
235
236 // 黑体
237 { SIMHEI, "Simhei" },
238 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
jshin7476cf52014-09-09 12:30:57 -0700239 { SIMHEI, "Noto Sans CJK SC" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000240 { SIMHEI, "MYingHeiGB18030" },
241 { SIMHEI, "MYingHeiB5HK" },
242
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000243 // 新細明體
244 { PMINGLIU, "PMingLiU"},
245 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
246 { PMINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000247
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000248 // 細明體
249 { MINGLIU, "MingLiU"},
250 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
251 { MINGLIU, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000252
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000253 // 新細明體
254 { PMINGLIUHK, "PMingLiU_HKSCS"},
255 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
256 { PMINGLIUHK, "MSung B5HK"},
reed@google.comb1c65b62013-02-26 15:50:51 +0000257
commit-bot@chromium.orgc4de7762013-03-20 13:33:33 +0000258 // 細明體
259 { MINGLIUHK, "MingLiU_HKSCS"},
260 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
261 { MINGLIUHK, "MSung B5HK"},
262
263 // Cambria
264 { CAMBRIA, "Cambria" },
265 { CAMBRIA, "Caladea" },
bungeman@google.comc526c712013-09-20 17:41:52 +0000266
267 // Calibri
268 { CALIBRI, "Calibri" },
269 { CALIBRI, "Carlito" },
reed@google.comb1c65b62013-02-26 15:50:51 +0000270 };
271
272 static const size_t kFontCount =
273 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
274
275 // TODO(jungshik): If this loop turns out to be hot, turn
276 // the array to a static (hash)map to speed it up.
277 for (size_t i = 0; i < kFontCount; ++i) {
278 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
279 return kFontEquivMap[i].clazz;
280 }
281 return OTHER;
282}
283
284
285// Return true if |font_a| and |font_b| are visually and at the metrics
286// level interchangeable.
287bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
288{
289 FontEquivClass class_a = GetFontEquivClass(font_a);
290 FontEquivClass class_b = GetFontEquivClass(font_b);
291
292 return class_a != OTHER && class_a == class_b;
293}
294
reed@google.comb1c65b62013-02-26 15:50:51 +0000295// Normally we only return exactly the font asked for. In last-resort
296// cases, the request either doesn't specify a font or is one of the
297// basic font names like "Sans", "Serif" or "Monospace". This function
298// tells you whether a given request is for such a fallback.
mtkleinba59a672014-08-04 10:18:27 -0700299bool IsFallbackFontAllowed(const SkString& family) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000300 const char* family_cstr = family.c_str();
mtkleinba59a672014-08-04 10:18:27 -0700301 return family.isEmpty() ||
reed@google.comb1c65b62013-02-26 15:50:51 +0000302 strcasecmp(family_cstr, "sans") == 0 ||
303 strcasecmp(family_cstr, "serif") == 0 ||
304 strcasecmp(family_cstr, "monospace") == 0;
305}
306
reed@google.comb1c65b62013-02-26 15:50:51 +0000307// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
bungeman11a77c62016-04-12 13:45:06 -0700308#ifdef SK_VERY_LEGACY_CREATE_TYPEFACE
reed@google.comf71a2332013-02-27 19:06:30 +0000309SkTypeface::Style GetFontStyle(FcPattern* font) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000310 int resulting_bold;
311 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
312 resulting_bold = FC_WEIGHT_NORMAL;
313
314 int resulting_italic;
315 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
316 resulting_italic = FC_SLANT_ROMAN;
317
318 // If we ask for an italic font, fontconfig might take a roman font and set
319 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
320 // that the font is italic or oblique. So, if we see a matrix, we don't
321 // believe that it's italic.
322 FcValue matrix;
323 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
324
325 // If we ask for an italic font, fontconfig might take a roman font and set
326 // FC_EMBOLDEN.
327 FcValue embolden;
328 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
329
330 int styleBits = 0;
331 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
332 styleBits |= SkTypeface::kBold;
333 }
334 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
335 styleBits |= SkTypeface::kItalic;
336 }
337
reed@google.comf71a2332013-02-27 19:06:30 +0000338 return (SkTypeface::Style)styleBits;
reed@google.comb1c65b62013-02-26 15:50:51 +0000339}
bungeman11a77c62016-04-12 13:45:06 -0700340#else
341
342static int get_int(FcPattern* pattern, const char object[], int missing) {
343 int value;
344 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
345 return missing;
346 }
347 return value;
348}
349
350static int map_range(SkFixed value,
351 SkFixed old_min, SkFixed old_max,
352 SkFixed new_min, SkFixed new_max)
353{
354 SkASSERT(old_min < old_max);
355 SkASSERT(new_min <= new_max);
356 return new_min + SkMulDiv(value - old_min, new_max - new_min, old_max - old_min);
357}
358
359struct MapRanges {
360 SkFixed old_val;
361 SkFixed new_val;
362};
363
364static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int rangesCount) {
365 // -Inf to [0]
366 if (val < ranges[0].old_val) {
367 return ranges[0].new_val;
368 }
369
370 // Linear from [i] to [i+1]
371 for (int i = 0; i < rangesCount - 1; ++i) {
372 if (val < ranges[i+1].old_val) {
373 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
374 ranges[i].new_val, ranges[i+1].new_val);
375 }
376 }
377
378 // From [n] to +Inf
379 // if (fcweight < Inf)
380 return ranges[rangesCount-1].new_val;
381}
382
383static int map_ranges(int val, MapRanges const ranges[], int rangesCount) {
384 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesCount));
385}
386
387template<int n> struct SkTFixed {
388 static_assert(-32768 <= n && n <= 32767, "SkTFixed_n_not_in_range");
389 static const SkFixed value = static_cast<SkFixed>(n << 16);
390};
391
392static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
393 typedef SkFontStyle SkFS;
394
395 static const MapRanges weightRanges[] = {
396 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value },
397 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weight>::value },
398 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value },
399 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::value },
400 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::value },
401 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>::value },
402 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
403 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
404 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
405 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<1000>::value },
406 };
407 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
408 weightRanges, SK_ARRAY_COUNT(weightRanges));
409
410 static const MapRanges widthRanges[] = {
411 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondensed_Width>::value },
412 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondensed_Width>::value },
413 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>::value },
414 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed_Width>::value },
415 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value },
416 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_Width>::value },
417 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::value },
418 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded_Width>::value },
419 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltaExpanded_Width>::value },
420 };
421 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
422 widthRanges, SK_ARRAY_COUNT(widthRanges));
423
424 SkFS::Slant slant = get_int(pattern, FC_SLANT, FC_SLANT_ROMAN) > 0
425 ? SkFS::kItalic_Slant
426 : SkFS::kUpright_Slant;
427
428 return SkFontStyle(weight, width, slant);
429}
430
431static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
432 typedef SkFontStyle SkFS;
433
434 static const MapRanges weightRanges[] = {
435 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value },
436 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIGHT>::value },
437 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value },
438 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::value },
439 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::value },
440 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>::value },
441 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
442 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
443 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
444 { SkTFixed<1000>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
445 };
446 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
447
448 static const MapRanges widthRanges[] = {
449 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRACONDENSED>::value },
450 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRACONDENSED>::value },
451 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>::value },
452 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICONDENSED>::value },
453 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value },
454 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPANDED>::value },
455 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::value },
456 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value },
457 { SkTFixed<SkFS::kUltaExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value },
458 };
459 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges));
460
461 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
462 FcPatternAddInteger(pattern, FC_WIDTH, width);
463 FcPatternAddInteger(pattern, FC_SLANT, style.isItalic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
464}
465
466SkFontStyle GetFontStyle(FcPattern* font) {
467 return skfontstyle_from_fcpattern(font);
468}
469#endif
reed@google.comb1c65b62013-02-26 15:50:51 +0000470
471} // anonymous namespace
472
473///////////////////////////////////////////////////////////////////////////////
474
reed@google.comf71a2332013-02-27 19:06:30 +0000475#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000476
reed@google.comf71a2332013-02-27 19:06:30 +0000477SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000478 SkAutoMutexAcquire ac(mutex_);
479
reed@google.comb1c65b62013-02-26 15:50:51 +0000480 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000481
482 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000483}
484
485SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
486}
487
benjaminwagner2211a7b2015-12-01 11:12:05 -0800488bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
489 if (access(filename, R_OK) != 0) {
490 return false;
491 }
492 return true;
493}
494
495bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
496#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
497 FcBool is_scalable;
498 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
499 || !is_scalable) {
500 return false;
501 }
502#endif
503
504 // fontconfig can also return fonts which are unreadable
505 const char* c_filename = get_name(pattern, FC_FILE);
506 if (!c_filename) {
507 return false;
508 }
509 return this->isAccessible(c_filename);
510}
511
512// Find matching font from |font_set| for the given font family.
513FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
514 const char* post_config_family,
515 const SkString& family) {
516 // Older versions of fontconfig have a bug where they cannot select
517 // only scalable fonts so we have to manually filter the results.
518 FcPattern* match = nullptr;
519 for (int i = 0; i < font_set->nfont; ++i) {
520 FcPattern* current = font_set->fonts[i];
521 if (this->isValidPattern(current)) {
522 match = current;
523 break;
524 }
525 }
526
527 if (match && !IsFallbackFontAllowed(family)) {
528 bool acceptable_substitute = false;
529 for (int id = 0; id < 255; ++id) {
530 const char* post_match_family = get_name(match, FC_FAMILY, id);
531 if (!post_match_family)
532 break;
533 acceptable_substitute =
534 (strcasecmp(post_config_family, post_match_family) == 0 ||
535 // Workaround for Issue 12530:
536 // requested family: "Bitstream Vera Sans"
537 // post_config_family: "Arial"
538 // post_match_family: "Bitstream Vera Sans"
539 // -> We should treat this case as a good match.
540 strcasecmp(family.c_str(), post_match_family) == 0) ||
541 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
542 if (acceptable_substitute)
543 break;
544 }
545 if (!acceptable_substitute)
546 return nullptr;
547 }
548
549 return match;
550}
551
bungeman11a77c62016-04-12 13:45:06 -0700552#ifdef SK_VERY_LEGACY_CREATE_TYPEFACE
reed@google.comf71a2332013-02-27 19:06:30 +0000553bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
554 SkTypeface::Style style,
555 FontIdentity* outIdentity,
556 SkString* outFamilyName,
557 SkTypeface::Style* outStyle) {
bungeman11a77c62016-04-12 13:45:06 -0700558#else
559bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
560 SkFontStyle style,
561 FontIdentity* outIdentity,
562 SkString* outFamilyName,
563 SkFontStyle* outStyle) {
564#endif
mtkleinba59a672014-08-04 10:18:27 -0700565 SkString familyStr(familyName ? familyName : "");
566 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000567 return false;
568 }
569
570 SkAutoMutexAcquire ac(mutex_);
571
reed@google.comb1c65b62013-02-26 15:50:51 +0000572 FcPattern* pattern = FcPatternCreate();
573
reed@google.comee619a02013-02-26 22:58:09 +0000574 if (familyName) {
575 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
576 }
bungeman11a77c62016-04-12 13:45:06 -0700577#ifdef SK_VERY_LEGACY_CREATE_TYPEFACE
reed@google.comb1c65b62013-02-26 15:50:51 +0000578 FcPatternAddInteger(pattern, FC_WEIGHT,
579 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
580 : FC_WEIGHT_NORMAL);
581 FcPatternAddInteger(pattern, FC_SLANT,
582 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
583 : FC_SLANT_ROMAN);
bungeman11a77c62016-04-12 13:45:06 -0700584#else
585 fcpattern_from_skfontstyle(style, pattern);
586#endif
587
reed@google.comb1c65b62013-02-26 15:50:51 +0000588 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
589
halcanary96fcdcc2015-08-27 07:41:13 -0700590 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000591 FcDefaultSubstitute(pattern);
592
593 // Font matching:
594 // CSS often specifies a fallback list of families:
595 // font-family: a, b, c, serif;
596 // However, fontconfig will always do its best to find *a* font when asked
597 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700598 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000599 // and lets WebKit know to try the next CSS family name. However, fontconfig
600 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
601 // wish to support that.
602 //
603 // Thus, if a specific family is requested we set @family_requested. Then we
604 // record two strings: the family name after config processing and the
605 // family name after resolving. If the two are equal, it's a good match.
606 //
607 // So consider the case where a user has mapped Arial to Helvetica in their
608 // config.
609 // requested family: "Arial"
610 // post_config_family: "Helvetica"
611 // post_match_family: "Helvetica"
612 // -> good match
613 //
614 // and for a missing font:
615 // requested family: "Monaco"
616 // post_config_family: "Monaco"
617 // post_match_family: "Times New Roman"
618 // -> BAD match
619 //
620 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000621
622 const char* post_config_family = get_name(pattern, FC_FAMILY);
623 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000624 // we can just continue with an empty name, e.g. default font
625 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000626 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000627
628 FcResult result;
629 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
630 if (!font_set) {
631 FcPatternDestroy(pattern);
632 return false;
633 }
634
benjaminwagner2211a7b2015-12-01 11:12:05 -0800635 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000636 if (!match) {
637 FcPatternDestroy(pattern);
638 FcFontSetDestroy(font_set);
639 return false;
640 }
641
642 FcPatternDestroy(pattern);
643
reed@google.comf71a2332013-02-27 19:06:30 +0000644 // From here out we just extract our results from 'match'
645
reed@google.come49d67e2013-04-22 18:00:06 +0000646 post_config_family = get_name(match, FC_FAMILY);
647 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000648 FcFontSetDestroy(font_set);
649 return false;
650 }
651
reed@google.come49d67e2013-04-22 18:00:06 +0000652 const char* c_filename = get_name(match, FC_FILE);
653 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000654 FcFontSetDestroy(font_set);
655 return false;
656 }
reed@google.comf71a2332013-02-27 19:06:30 +0000657
reed@google.comb1c65b62013-02-26 15:50:51 +0000658 int face_index;
659 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
660 FcFontSetDestroy(font_set);
661 return false;
662 }
663
reed@google.comb1c65b62013-02-26 15:50:51 +0000664 FcFontSetDestroy(font_set);
665
reed@google.comf71a2332013-02-27 19:06:30 +0000666 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000667 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000668 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000669 }
reed@google.comf71a2332013-02-27 19:06:30 +0000670 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000671 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000672 }
reed@google.comf71a2332013-02-27 19:06:30 +0000673 if (outStyle) {
674 *outStyle = GetFontStyle(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000675 }
reed@google.comee619a02013-02-26 22:58:09 +0000676 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000677}
678
bungeman5f213d92015-01-27 05:39:10 -0800679SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000680 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000681}
reed@google.com027fd202013-04-19 20:45:30 +0000682
683///////////////////////////////////////////////////////////////////////////////
684
reed@google.com027fd202013-04-19 20:45:30 +0000685static bool find_name(const SkTDArray<const char*>& list, const char* str) {
686 int count = list.count();
687 for (int i = 0; i < count; ++i) {
688 if (!strcmp(list[i], str)) {
689 return true;
690 }
691 }
692 return false;
693}
694
695SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000696 SkAutoMutexAcquire ac(mutex_);
697
reed@google.com027fd202013-04-19 20:45:30 +0000698 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000699 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
halcanary96fcdcc2015-08-27 07:41:13 -0700700 if (nullptr == pat) {
701 return nullptr;
bungeman@google.com02dd6882013-07-22 14:10:08 +0000702 }
703
704 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
705 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
halcanary96fcdcc2015-08-27 07:41:13 -0700706 if (nullptr == os) {
707 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000708 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000709
halcanary96fcdcc2015-08-27 07:41:13 -0700710 FcFontSet* fs = FcFontList(nullptr, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000711 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
halcanary96fcdcc2015-08-27 07:41:13 -0700712 if (nullptr == fs) {
713 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000714 }
715
716 SkTDArray<const char*> names;
717 SkTDArray<size_t> sizes;
718 for (int i = 0; i < fs->nfont; ++i) {
719 FcPattern* match = fs->fonts[i];
720 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000721 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000722 *names.append() = famName;
723 *sizes.append() = strlen(famName) + 1;
724 }
725 }
726
reed@google.com027fd202013-04-19 20:45:30 +0000727 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
728 sizes.begin(), names.count());
729}