blob: cc80fbe647c212e14cfaf71430122c8475d850f2 [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 -0700308static int get_int(FcPattern* pattern, const char object[], int missing) {
309 int value;
310 if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) {
311 return missing;
312 }
313 return value;
314}
315
316static int map_range(SkFixed value,
317 SkFixed old_min, SkFixed old_max,
318 SkFixed new_min, SkFixed new_max)
319{
320 SkASSERT(old_min < old_max);
321 SkASSERT(new_min <= new_max);
322 return new_min + SkMulDiv(value - old_min, new_max - new_min, old_max - old_min);
323}
324
325struct MapRanges {
326 SkFixed old_val;
327 SkFixed new_val;
328};
329
330static SkFixed map_ranges_fixed(SkFixed val, MapRanges const ranges[], int rangesCount) {
331 // -Inf to [0]
332 if (val < ranges[0].old_val) {
333 return ranges[0].new_val;
334 }
335
336 // Linear from [i] to [i+1]
337 for (int i = 0; i < rangesCount - 1; ++i) {
338 if (val < ranges[i+1].old_val) {
339 return map_range(val, ranges[i].old_val, ranges[i+1].old_val,
340 ranges[i].new_val, ranges[i+1].new_val);
341 }
342 }
343
344 // From [n] to +Inf
345 // if (fcweight < Inf)
346 return ranges[rangesCount-1].new_val;
347}
348
349static int map_ranges(int val, MapRanges const ranges[], int rangesCount) {
350 return SkFixedRoundToInt(map_ranges_fixed(SkIntToFixed(val), ranges, rangesCount));
351}
352
353template<int n> struct SkTFixed {
354 static_assert(-32768 <= n && n <= 32767, "SkTFixed_n_not_in_range");
355 static const SkFixed value = static_cast<SkFixed>(n << 16);
356};
357
358static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) {
359 typedef SkFontStyle SkFS;
360
361 static const MapRanges weightRanges[] = {
362 { SkTFixed<FC_WEIGHT_THIN>::value, SkTFixed<SkFS::kThin_Weight>::value },
363 { SkTFixed<FC_WEIGHT_EXTRALIGHT>::value, SkTFixed<SkFS::kExtraLight_Weight>::value },
364 { SkTFixed<FC_WEIGHT_LIGHT>::value, SkTFixed<SkFS::kLight_Weight>::value },
365 { SkTFixed<FC_WEIGHT_REGULAR>::value, SkTFixed<SkFS::kNormal_Weight>::value },
366 { SkTFixed<FC_WEIGHT_MEDIUM>::value, SkTFixed<SkFS::kMedium_Weight>::value },
367 { SkTFixed<FC_WEIGHT_DEMIBOLD>::value, SkTFixed<SkFS::kSemiBold_Weight>::value },
368 { SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
369 { SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
370 { SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
371 { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<1000>::value },
372 };
373 int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
374 weightRanges, SK_ARRAY_COUNT(weightRanges));
375
376 static const MapRanges widthRanges[] = {
377 { SkTFixed<FC_WIDTH_ULTRACONDENSED>::value, SkTFixed<SkFS::kUltraCondensed_Width>::value },
378 { SkTFixed<FC_WIDTH_EXTRACONDENSED>::value, SkTFixed<SkFS::kExtraCondensed_Width>::value },
379 { SkTFixed<FC_WIDTH_CONDENSED>::value, SkTFixed<SkFS::kCondensed_Width>::value },
380 { SkTFixed<FC_WIDTH_SEMICONDENSED>::value, SkTFixed<SkFS::kSemiCondensed_Width>::value },
381 { SkTFixed<FC_WIDTH_NORMAL>::value, SkTFixed<SkFS::kNormal_Width>::value },
382 { SkTFixed<FC_WIDTH_SEMIEXPANDED>::value, SkTFixed<SkFS::kSemiExpanded_Width>::value },
383 { SkTFixed<FC_WIDTH_EXPANDED>::value, SkTFixed<SkFS::kExpanded_Width>::value },
384 { SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value, SkTFixed<SkFS::kExtraExpanded_Width>::value },
385 { SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value, SkTFixed<SkFS::kUltaExpanded_Width>::value },
386 };
387 int width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL),
388 widthRanges, SK_ARRAY_COUNT(widthRanges));
389
390 SkFS::Slant slant = get_int(pattern, FC_SLANT, FC_SLANT_ROMAN) > 0
391 ? SkFS::kItalic_Slant
392 : SkFS::kUpright_Slant;
393
394 return SkFontStyle(weight, width, slant);
395}
396
397static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
398 typedef SkFontStyle SkFS;
399
400 static const MapRanges weightRanges[] = {
401 { SkTFixed<SkFS::kThin_Weight>::value, SkTFixed<FC_WEIGHT_THIN>::value },
402 { SkTFixed<SkFS::kExtraLight_Weight>::value, SkTFixed<FC_WEIGHT_EXTRALIGHT>::value },
403 { SkTFixed<SkFS::kLight_Weight>::value, SkTFixed<FC_WEIGHT_LIGHT>::value },
404 { SkTFixed<SkFS::kNormal_Weight>::value, SkTFixed<FC_WEIGHT_REGULAR>::value },
405 { SkTFixed<SkFS::kMedium_Weight>::value, SkTFixed<FC_WEIGHT_MEDIUM>::value },
406 { SkTFixed<SkFS::kSemiBold_Weight>::value, SkTFixed<FC_WEIGHT_DEMIBOLD>::value },
407 { SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
408 { SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
409 { SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
410 { SkTFixed<1000>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
411 };
412 int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
413
414 static const MapRanges widthRanges[] = {
415 { SkTFixed<SkFS::kUltraCondensed_Width>::value, SkTFixed<FC_WIDTH_ULTRACONDENSED>::value },
416 { SkTFixed<SkFS::kExtraCondensed_Width>::value, SkTFixed<FC_WIDTH_EXTRACONDENSED>::value },
417 { SkTFixed<SkFS::kCondensed_Width>::value, SkTFixed<FC_WIDTH_CONDENSED>::value },
418 { SkTFixed<SkFS::kSemiCondensed_Width>::value, SkTFixed<FC_WIDTH_SEMICONDENSED>::value },
419 { SkTFixed<SkFS::kNormal_Width>::value, SkTFixed<FC_WIDTH_NORMAL>::value },
420 { SkTFixed<SkFS::kSemiExpanded_Width>::value, SkTFixed<FC_WIDTH_SEMIEXPANDED>::value },
421 { SkTFixed<SkFS::kExpanded_Width>::value, SkTFixed<FC_WIDTH_EXPANDED>::value },
422 { SkTFixed<SkFS::kExtraExpanded_Width>::value, SkTFixed<FC_WIDTH_EXTRAEXPANDED>::value },
423 { SkTFixed<SkFS::kUltaExpanded_Width>::value, SkTFixed<FC_WIDTH_ULTRAEXPANDED>::value },
424 };
425 int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges));
426
427 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
428 FcPatternAddInteger(pattern, FC_WIDTH, width);
429 FcPatternAddInteger(pattern, FC_SLANT, style.isItalic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
430}
431
reed@google.comb1c65b62013-02-26 15:50:51 +0000432} // anonymous namespace
433
434///////////////////////////////////////////////////////////////////////////////
435
reed@google.comf71a2332013-02-27 19:06:30 +0000436#define kMaxFontFamilyLength 2048
reed@google.comb1c65b62013-02-26 15:50:51 +0000437
reed@google.comf71a2332013-02-27 19:06:30 +0000438SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
reed@google.comf55061f2013-04-22 18:48:45 +0000439 SkAutoMutexAcquire ac(mutex_);
440
reed@google.comb1c65b62013-02-26 15:50:51 +0000441 FcInit();
reed@google.comf55061f2013-04-22 18:48:45 +0000442
443 SkDEBUGCODE(fontconfiginterface_unittest();)
reed@google.comb1c65b62013-02-26 15:50:51 +0000444}
445
446SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
447}
448
benjaminwagner2211a7b2015-12-01 11:12:05 -0800449bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) {
450 if (access(filename, R_OK) != 0) {
451 return false;
452 }
453 return true;
454}
455
456bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) {
457#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
458 FcBool is_scalable;
459 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
460 || !is_scalable) {
461 return false;
462 }
463#endif
464
465 // fontconfig can also return fonts which are unreadable
466 const char* c_filename = get_name(pattern, FC_FILE);
467 if (!c_filename) {
468 return false;
469 }
470 return this->isAccessible(c_filename);
471}
472
473// Find matching font from |font_set| for the given font family.
474FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set,
475 const char* post_config_family,
476 const SkString& family) {
477 // Older versions of fontconfig have a bug where they cannot select
478 // only scalable fonts so we have to manually filter the results.
479 FcPattern* match = nullptr;
480 for (int i = 0; i < font_set->nfont; ++i) {
481 FcPattern* current = font_set->fonts[i];
482 if (this->isValidPattern(current)) {
483 match = current;
484 break;
485 }
486 }
487
488 if (match && !IsFallbackFontAllowed(family)) {
489 bool acceptable_substitute = false;
490 for (int id = 0; id < 255; ++id) {
491 const char* post_match_family = get_name(match, FC_FAMILY, id);
492 if (!post_match_family)
493 break;
494 acceptable_substitute =
495 (strcasecmp(post_config_family, post_match_family) == 0 ||
496 // Workaround for Issue 12530:
497 // requested family: "Bitstream Vera Sans"
498 // post_config_family: "Arial"
499 // post_match_family: "Bitstream Vera Sans"
500 // -> We should treat this case as a good match.
501 strcasecmp(family.c_str(), post_match_family) == 0) ||
502 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
503 if (acceptable_substitute)
504 break;
505 }
506 if (!acceptable_substitute)
507 return nullptr;
508 }
509
510 return match;
511}
512
bungeman11a77c62016-04-12 13:45:06 -0700513bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
514 SkFontStyle style,
515 FontIdentity* outIdentity,
516 SkString* outFamilyName,
517 SkFontStyle* outStyle) {
mtkleinba59a672014-08-04 10:18:27 -0700518 SkString familyStr(familyName ? familyName : "");
519 if (familyStr.size() > kMaxFontFamilyLength) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000520 return false;
521 }
522
523 SkAutoMutexAcquire ac(mutex_);
524
reed@google.comb1c65b62013-02-26 15:50:51 +0000525 FcPattern* pattern = FcPatternCreate();
526
reed@google.comee619a02013-02-26 22:58:09 +0000527 if (familyName) {
528 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
529 }
bungeman11a77c62016-04-12 13:45:06 -0700530 fcpattern_from_skfontstyle(style, pattern);
bungeman11a77c62016-04-12 13:45:06 -0700531
reed@google.comb1c65b62013-02-26 15:50:51 +0000532 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
533
halcanary96fcdcc2015-08-27 07:41:13 -0700534 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
reed@google.comb1c65b62013-02-26 15:50:51 +0000535 FcDefaultSubstitute(pattern);
536
537 // Font matching:
538 // CSS often specifies a fallback list of families:
539 // font-family: a, b, c, serif;
540 // However, fontconfig will always do its best to find *a* font when asked
541 // for something so we need a way to tell if the match which it has found is
halcanary96fcdcc2015-08-27 07:41:13 -0700542 // "good enough" for us. Otherwise, we can return nullptr which gets piped up
reed@google.comb1c65b62013-02-26 15:50:51 +0000543 // and lets WebKit know to try the next CSS family name. However, fontconfig
544 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
545 // wish to support that.
546 //
547 // Thus, if a specific family is requested we set @family_requested. Then we
548 // record two strings: the family name after config processing and the
549 // family name after resolving. If the two are equal, it's a good match.
550 //
551 // So consider the case where a user has mapped Arial to Helvetica in their
552 // config.
553 // requested family: "Arial"
554 // post_config_family: "Helvetica"
555 // post_match_family: "Helvetica"
556 // -> good match
557 //
558 // and for a missing font:
559 // requested family: "Monaco"
560 // post_config_family: "Monaco"
561 // post_match_family: "Times New Roman"
562 // -> BAD match
563 //
564 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
reed@google.come49d67e2013-04-22 18:00:06 +0000565
566 const char* post_config_family = get_name(pattern, FC_FAMILY);
567 if (!post_config_family) {
reed@google.comab792822013-04-23 16:35:09 +0000568 // we can just continue with an empty name, e.g. default font
569 post_config_family = "";
reed@google.come49d67e2013-04-22 18:00:06 +0000570 }
reed@google.comb1c65b62013-02-26 15:50:51 +0000571
572 FcResult result;
573 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
574 if (!font_set) {
575 FcPatternDestroy(pattern);
576 return false;
577 }
578
benjaminwagner2211a7b2015-12-01 11:12:05 -0800579 FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr);
reed@google.comb1c65b62013-02-26 15:50:51 +0000580 if (!match) {
581 FcPatternDestroy(pattern);
582 FcFontSetDestroy(font_set);
583 return false;
584 }
585
586 FcPatternDestroy(pattern);
587
reed@google.comf71a2332013-02-27 19:06:30 +0000588 // From here out we just extract our results from 'match'
589
reed@google.come49d67e2013-04-22 18:00:06 +0000590 post_config_family = get_name(match, FC_FAMILY);
591 if (!post_config_family) {
reed@google.comf71a2332013-02-27 19:06:30 +0000592 FcFontSetDestroy(font_set);
593 return false;
594 }
595
reed@google.come49d67e2013-04-22 18:00:06 +0000596 const char* c_filename = get_name(match, FC_FILE);
597 if (!c_filename) {
reed@google.comb1c65b62013-02-26 15:50:51 +0000598 FcFontSetDestroy(font_set);
599 return false;
600 }
reed@google.comf71a2332013-02-27 19:06:30 +0000601
reed@google.comb1c65b62013-02-26 15:50:51 +0000602 int face_index;
603 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
604 FcFontSetDestroy(font_set);
605 return false;
606 }
607
reed@google.comb1c65b62013-02-26 15:50:51 +0000608 FcFontSetDestroy(font_set);
609
reed@google.comf71a2332013-02-27 19:06:30 +0000610 if (outIdentity) {
reed@google.com8c9737e2013-03-06 13:06:03 +0000611 outIdentity->fTTCIndex = face_index;
reed@google.come49d67e2013-04-22 18:00:06 +0000612 outIdentity->fString.set(c_filename);
reed@google.comb1c65b62013-02-26 15:50:51 +0000613 }
reed@google.comf71a2332013-02-27 19:06:30 +0000614 if (outFamilyName) {
reed@google.come49d67e2013-04-22 18:00:06 +0000615 outFamilyName->set(post_config_family);
reed@google.comb1c65b62013-02-26 15:50:51 +0000616 }
reed@google.comf71a2332013-02-27 19:06:30 +0000617 if (outStyle) {
bungemaned2edab2016-04-13 05:23:35 -0700618 *outStyle = skfontstyle_from_fcpattern(match);
reed@google.comb1c65b62013-02-26 15:50:51 +0000619 }
reed@google.comee619a02013-02-26 22:58:09 +0000620 return true;
reed@google.comb1c65b62013-02-26 15:50:51 +0000621}
622
bungeman5f213d92015-01-27 05:39:10 -0800623SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
reed@google.com8c3f84d2013-03-19 13:34:55 +0000624 return SkStream::NewFromFile(identity.fString.c_str());
reed@google.comb1c65b62013-02-26 15:50:51 +0000625}
reed@google.com027fd202013-04-19 20:45:30 +0000626
627///////////////////////////////////////////////////////////////////////////////
628
reed@google.com027fd202013-04-19 20:45:30 +0000629static bool find_name(const SkTDArray<const char*>& list, const char* str) {
630 int count = list.count();
631 for (int i = 0; i < count; ++i) {
632 if (!strcmp(list[i], str)) {
633 return true;
634 }
635 }
636 return false;
637}
638
639SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
reed@google.comf55061f2013-04-22 18:48:45 +0000640 SkAutoMutexAcquire ac(mutex_);
641
reed@google.com027fd202013-04-19 20:45:30 +0000642 FcPattern* pat = FcPatternCreate();
bungeman@google.com02dd6882013-07-22 14:10:08 +0000643 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
halcanary96fcdcc2015-08-27 07:41:13 -0700644 if (nullptr == pat) {
645 return nullptr;
bungeman@google.com02dd6882013-07-22 14:10:08 +0000646 }
647
648 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
649 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
halcanary96fcdcc2015-08-27 07:41:13 -0700650 if (nullptr == os) {
651 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000652 }
bungeman@google.com02dd6882013-07-22 14:10:08 +0000653
halcanary96fcdcc2015-08-27 07:41:13 -0700654 FcFontSet* fs = FcFontList(nullptr, pat, os);
bungeman@google.com02dd6882013-07-22 14:10:08 +0000655 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
halcanary96fcdcc2015-08-27 07:41:13 -0700656 if (nullptr == fs) {
657 return nullptr;
reed@google.com027fd202013-04-19 20:45:30 +0000658 }
659
660 SkTDArray<const char*> names;
661 SkTDArray<size_t> sizes;
662 for (int i = 0; i < fs->nfont; ++i) {
663 FcPattern* match = fs->fonts[i];
664 const char* famName = get_name(match, FC_FAMILY);
reed@google.come49d67e2013-04-22 18:00:06 +0000665 if (famName && !find_name(names, famName)) {
reed@google.com027fd202013-04-19 20:45:30 +0000666 *names.append() = famName;
667 *sizes.append() = strlen(famName) + 1;
668 }
669 }
670
reed@google.com027fd202013-04-19 20:45:30 +0000671 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
672 sizes.begin(), names.count());
673}