1 | /* |
2 | * Copyright 2009-2015 Google Inc. |
3 | * |
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 | |
10 | #include "include/core/SkFontStyle.h" |
11 | #include "include/core/SkStream.h" |
12 | #include "include/core/SkString.h" |
13 | #include "include/core/SkTypeface.h" |
14 | #include "include/private/SkFixed.h" |
15 | #include "include/private/SkMutex.h" |
16 | #include "include/private/SkTArray.h" |
17 | #include "include/private/SkTDArray.h" |
18 | #include "include/private/SkTemplates.h" |
19 | #include "src/core/SkAutoMalloc.h" |
20 | #include "src/core/SkBuffer.h" |
21 | #include "src/ports/SkFontConfigInterface_direct.h" |
22 | |
23 | #include <fontconfig/fontconfig.h> |
24 | #include <unistd.h> |
25 | |
26 | namespace { |
27 | |
28 | // Fontconfig is not threadsafe before 2.10.91. Before that, we lock with a global mutex. |
29 | // See https://bug.skia.org/1497 for background. |
30 | static SkMutex& f_c_mutex() { |
31 | static SkMutex& mutex = *(new SkMutex); |
32 | return mutex; |
33 | } |
34 | |
35 | struct FCLocker { |
36 | // Assume FcGetVersion() has always been thread safe. |
37 | |
38 | FCLocker() { |
39 | if (FcGetVersion() < 21091) { |
40 | f_c_mutex().acquire(); |
41 | } |
42 | } |
43 | |
44 | ~FCLocker() { |
45 | AssertHeld(); |
46 | if (FcGetVersion() < 21091) { |
47 | f_c_mutex().release(); |
48 | } |
49 | } |
50 | |
51 | static void AssertHeld() { SkDEBUGCODE( |
52 | if (FcGetVersion() < 21091) { |
53 | f_c_mutex().assertHeld(); |
54 | } |
55 | ) } |
56 | }; |
57 | |
58 | using UniqueFCConfig = std::unique_ptr<FcConfig, SkFunctionWrapper<decltype(FcConfigDestroy), FcConfigDestroy>>; |
59 | |
60 | } // namespace |
61 | |
62 | size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const { |
63 | size_t size = sizeof(fID) + sizeof(fTTCIndex); |
64 | size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic |
65 | size += sizeof(int32_t) + fString.size(); // store length+data |
66 | if (addr) { |
67 | SkWBuffer buffer(addr, size); |
68 | |
69 | buffer.write32(fID); |
70 | buffer.write32(fTTCIndex); |
71 | buffer.write32(fString.size()); |
72 | buffer.write32(fStyle.weight()); |
73 | buffer.write32(fStyle.width()); |
74 | buffer.write8(fStyle.slant()); |
75 | buffer.write(fString.c_str(), fString.size()); |
76 | buffer.padToAlign4(); |
77 | |
78 | SkASSERT(buffer.pos() == size); |
79 | } |
80 | return size; |
81 | } |
82 | |
83 | size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr, |
84 | size_t size) { |
85 | SkRBuffer buffer(addr, size); |
86 | |
87 | (void)buffer.readU32(&fID); |
88 | (void)buffer.readS32(&fTTCIndex); |
89 | uint32_t strLen, weight, width; |
90 | (void)buffer.readU32(&strLen); |
91 | (void)buffer.readU32(&weight); |
92 | (void)buffer.readU32(&width); |
93 | uint8_t u8; |
94 | (void)buffer.readU8(&u8); |
95 | SkFontStyle::Slant slant = (SkFontStyle::Slant)u8; |
96 | fStyle = SkFontStyle(weight, width, slant); |
97 | fString.resize(strLen); |
98 | (void)buffer.read(fString.writable_str(), strLen); |
99 | buffer.skipToAlign4(); |
100 | |
101 | return buffer.pos(); // the actual number of bytes read |
102 | } |
103 | |
104 | #ifdef SK_DEBUG |
105 | static void make_iden(SkFontConfigInterface::FontIdentity* iden) { |
106 | iden->fID = 10; |
107 | iden->fTTCIndex = 2; |
108 | iden->fString.set("Hello world" ); |
109 | iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant); |
110 | } |
111 | |
112 | static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0, |
113 | int initValue) { |
114 | SkFontConfigInterface::FontIdentity iden1; |
115 | |
116 | size_t size0 = iden0.writeToMemory(nullptr); |
117 | |
118 | SkAutoMalloc storage(size0); |
119 | memset(storage.get(), initValue, size0); |
120 | |
121 | size_t size1 = iden0.writeToMemory(storage.get()); |
122 | SkASSERT(size0 == size1); |
123 | |
124 | SkASSERT(iden0 != iden1); |
125 | size_t size2 = iden1.readFromMemory(storage.get(), size1); |
126 | SkASSERT(size2 == size1); |
127 | SkASSERT(iden0 == iden1); |
128 | } |
129 | |
130 | static void fontconfiginterface_unittest() { |
131 | SkFontConfigInterface::FontIdentity iden0, iden1; |
132 | |
133 | SkASSERT(iden0 == iden1); |
134 | |
135 | make_iden(&iden0); |
136 | SkASSERT(iden0 != iden1); |
137 | |
138 | make_iden(&iden1); |
139 | SkASSERT(iden0 == iden1); |
140 | |
141 | test_writeToMemory(iden0, 0); |
142 | test_writeToMemory(iden0, 0); |
143 | } |
144 | #endif |
145 | |
146 | /////////////////////////////////////////////////////////////////////////////// |
147 | |
148 | // Returns the string from the pattern, or nullptr |
149 | static const char* get_string(FcPattern* pattern, const char field[], int index = 0) { |
150 | const char* name; |
151 | if (FcPatternGetString(pattern, field, index, (FcChar8**)&name) != FcResultMatch) { |
152 | name = nullptr; |
153 | } |
154 | return name; |
155 | } |
156 | |
157 | /////////////////////////////////////////////////////////////////////////////// |
158 | |
159 | namespace { |
160 | |
161 | // Equivalence classes, used to match the Liberation and other fonts |
162 | // with their metric-compatible replacements. See the discussion in |
163 | // GetFontEquivClass(). |
164 | enum FontEquivClass |
165 | { |
166 | OTHER, |
167 | SANS, |
168 | SERIF, |
169 | MONO, |
170 | SYMBOL, |
171 | PGOTHIC, |
172 | GOTHIC, |
173 | PMINCHO, |
174 | MINCHO, |
175 | SIMSUN, |
176 | NSIMSUN, |
177 | SIMHEI, |
178 | PMINGLIU, |
179 | MINGLIU, |
180 | PMINGLIUHK, |
181 | MINGLIUHK, |
182 | CAMBRIA, |
183 | CALIBRI, |
184 | }; |
185 | |
186 | // Match the font name against a whilelist of fonts, returning the equivalence |
187 | // class. |
188 | FontEquivClass GetFontEquivClass(const char* fontname) |
189 | { |
190 | // It would be nice for fontconfig to tell us whether a given suggested |
191 | // replacement is a "strong" match (that is, an equivalent font) or |
192 | // a "weak" match (that is, fontconfig's next-best attempt at finding a |
193 | // substitute). However, I played around with the fontconfig API for |
194 | // a good few hours and could not make it reveal this information. |
195 | // |
196 | // So instead, we hardcode. Initially this function emulated |
197 | // /etc/fonts/conf.d/30-metric-aliases.conf |
198 | // from my Ubuntu system, but we're better off being very conservative. |
199 | |
200 | // Arimo, Tinos and Cousine are a set of fonts metric-compatible with |
201 | // Arial, Times New Roman and Courier New with a character repertoire |
202 | // much larger than Liberation. Note that Cousine is metrically |
203 | // compatible with Courier New, but the former is sans-serif while |
204 | // the latter is serif. |
205 | |
206 | |
207 | struct FontEquivMap { |
208 | FontEquivClass clazz; |
209 | const char name[40]; |
210 | }; |
211 | |
212 | static const FontEquivMap kFontEquivMap[] = { |
213 | { SANS, "Arial" }, |
214 | { SANS, "Arimo" }, |
215 | { SANS, "Liberation Sans" }, |
216 | |
217 | { SERIF, "Times New Roman" }, |
218 | { SERIF, "Tinos" }, |
219 | { SERIF, "Liberation Serif" }, |
220 | |
221 | { MONO, "Courier New" }, |
222 | { MONO, "Cousine" }, |
223 | { MONO, "Liberation Mono" }, |
224 | |
225 | { SYMBOL, "Symbol" }, |
226 | { SYMBOL, "Symbol Neu" }, |
227 | |
228 | // MS Pゴシック |
229 | { PGOTHIC, "MS PGothic" }, |
230 | { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" |
231 | "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, |
232 | { PGOTHIC, "Noto Sans CJK JP" }, |
233 | { PGOTHIC, "IPAPGothic" }, |
234 | { PGOTHIC, "MotoyaG04Gothic" }, |
235 | |
236 | // MS ゴシック |
237 | { GOTHIC, "MS Gothic" }, |
238 | { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 " |
239 | "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, |
240 | { GOTHIC, "Noto Sans Mono CJK JP" }, |
241 | { GOTHIC, "IPAGothic" }, |
242 | { GOTHIC, "MotoyaG04GothicMono" }, |
243 | |
244 | // MS P明朝 |
245 | { PMINCHO, "MS PMincho" }, |
246 | { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" |
247 | "\xe6\x98\x8e\xe6\x9c\x9d" }, |
248 | { PMINCHO, "Noto Serif CJK JP" }, |
249 | { PMINCHO, "IPAPMincho" }, |
250 | { PMINCHO, "MotoyaG04Mincho" }, |
251 | |
252 | // MS 明朝 |
253 | { MINCHO, "MS Mincho" }, |
254 | { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" }, |
255 | { MINCHO, "Noto Serif CJK JP" }, |
256 | { MINCHO, "IPAMincho" }, |
257 | { MINCHO, "MotoyaG04MinchoMono" }, |
258 | |
259 | // 宋体 |
260 | { SIMSUN, "Simsun" }, |
261 | { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" }, |
262 | { SIMSUN, "Noto Serif CJK SC" }, |
263 | { SIMSUN, "MSung GB18030" }, |
264 | { SIMSUN, "Song ASC" }, |
265 | |
266 | // 新宋体 |
267 | { NSIMSUN, "NSimsun" }, |
268 | { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" }, |
269 | { NSIMSUN, "Noto Serif CJK SC" }, |
270 | { NSIMSUN, "MSung GB18030" }, |
271 | { NSIMSUN, "N Song ASC" }, |
272 | |
273 | // 黑体 |
274 | { SIMHEI, "Simhei" }, |
275 | { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" }, |
276 | { SIMHEI, "Noto Sans CJK SC" }, |
277 | { SIMHEI, "MYingHeiGB18030" }, |
278 | { SIMHEI, "MYingHeiB5HK" }, |
279 | |
280 | // 新細明體 |
281 | { PMINGLIU, "PMingLiU" }, |
282 | { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, |
283 | { PMINGLIU, "Noto Serif CJK TC" }, |
284 | { PMINGLIU, "MSung B5HK" }, |
285 | |
286 | // 細明體 |
287 | { MINGLIU, "MingLiU" }, |
288 | { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, |
289 | { MINGLIU, "Noto Serif CJK TC" }, |
290 | { MINGLIU, "MSung B5HK" }, |
291 | |
292 | // 新細明體 |
293 | { PMINGLIUHK, "PMingLiU_HKSCS" }, |
294 | { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, |
295 | { PMINGLIUHK, "Noto Serif CJK TC" }, |
296 | { PMINGLIUHK, "MSung B5HK" }, |
297 | |
298 | // 細明體 |
299 | { MINGLIUHK, "MingLiU_HKSCS" }, |
300 | { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, |
301 | { MINGLIUHK, "Noto Serif CJK TC" }, |
302 | { MINGLIUHK, "MSung B5HK" }, |
303 | |
304 | // Cambria |
305 | { CAMBRIA, "Cambria" }, |
306 | { CAMBRIA, "Caladea" }, |
307 | |
308 | // Calibri |
309 | { CALIBRI, "Calibri" }, |
310 | { CALIBRI, "Carlito" }, |
311 | }; |
312 | |
313 | static const size_t kFontCount = |
314 | sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]); |
315 | |
316 | // TODO(jungshik): If this loop turns out to be hot, turn |
317 | // the array to a static (hash)map to speed it up. |
318 | for (size_t i = 0; i < kFontCount; ++i) { |
319 | if (strcasecmp(kFontEquivMap[i].name, fontname) == 0) |
320 | return kFontEquivMap[i].clazz; |
321 | } |
322 | return OTHER; |
323 | } |
324 | |
325 | |
326 | // Return true if |font_a| and |font_b| are visually and at the metrics |
327 | // level interchangeable. |
328 | bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b) |
329 | { |
330 | FontEquivClass class_a = GetFontEquivClass(font_a); |
331 | FontEquivClass class_b = GetFontEquivClass(font_b); |
332 | |
333 | return class_a != OTHER && class_a == class_b; |
334 | } |
335 | |
336 | // Normally we only return exactly the font asked for. In last-resort |
337 | // cases, the request either doesn't specify a font or is one of the |
338 | // basic font names like "Sans", "Serif" or "Monospace". This function |
339 | // tells you whether a given request is for such a fallback. |
340 | bool IsFallbackFontAllowed(const SkString& family) { |
341 | const char* family_cstr = family.c_str(); |
342 | return family.isEmpty() || |
343 | strcasecmp(family_cstr, "sans" ) == 0 || |
344 | strcasecmp(family_cstr, "serif" ) == 0 || |
345 | strcasecmp(family_cstr, "monospace" ) == 0; |
346 | } |
347 | |
348 | // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|. |
349 | static int get_int(FcPattern* pattern, const char object[], int missing) { |
350 | int value; |
351 | if (FcPatternGetInteger(pattern, object, 0, &value) != FcResultMatch) { |
352 | return missing; |
353 | } |
354 | return value; |
355 | } |
356 | |
357 | static int map_range(SkScalar value, |
358 | SkScalar old_min, SkScalar old_max, |
359 | SkScalar new_min, SkScalar new_max) |
360 | { |
361 | SkASSERT(old_min < old_max); |
362 | SkASSERT(new_min <= new_max); |
363 | return new_min + ((value - old_min) * (new_max - new_min) / (old_max - old_min)); |
364 | } |
365 | |
366 | struct MapRanges { |
367 | SkScalar old_val; |
368 | SkScalar new_val; |
369 | }; |
370 | |
371 | static SkScalar map_ranges(SkScalar val, MapRanges const ranges[], int rangesCount) { |
372 | // -Inf to [0] |
373 | if (val < ranges[0].old_val) { |
374 | return ranges[0].new_val; |
375 | } |
376 | |
377 | // Linear from [i] to [i+1] |
378 | for (int i = 0; i < rangesCount - 1; ++i) { |
379 | if (val < ranges[i+1].old_val) { |
380 | return map_range(val, ranges[i].old_val, ranges[i+1].old_val, |
381 | ranges[i].new_val, ranges[i+1].new_val); |
382 | } |
383 | } |
384 | |
385 | // From [n] to +Inf |
386 | // if (fcweight < Inf) |
387 | return ranges[rangesCount-1].new_val; |
388 | } |
389 | |
390 | #ifndef FC_WEIGHT_DEMILIGHT |
391 | #define FC_WEIGHT_DEMILIGHT 65 |
392 | #endif |
393 | |
394 | static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) { |
395 | typedef SkFontStyle SkFS; |
396 | |
397 | static constexpr MapRanges weightRanges[] = { |
398 | { FC_WEIGHT_THIN, SkFS::kThin_Weight }, |
399 | { FC_WEIGHT_EXTRALIGHT, SkFS::kExtraLight_Weight }, |
400 | { FC_WEIGHT_LIGHT, SkFS::kLight_Weight }, |
401 | { FC_WEIGHT_DEMILIGHT, 350 }, |
402 | { FC_WEIGHT_BOOK, 380 }, |
403 | { FC_WEIGHT_REGULAR, SkFS::kNormal_Weight }, |
404 | { FC_WEIGHT_MEDIUM, SkFS::kMedium_Weight }, |
405 | { FC_WEIGHT_DEMIBOLD, SkFS::kSemiBold_Weight }, |
406 | { FC_WEIGHT_BOLD, SkFS::kBold_Weight }, |
407 | { FC_WEIGHT_EXTRABOLD, SkFS::kExtraBold_Weight }, |
408 | { FC_WEIGHT_BLACK, SkFS::kBlack_Weight }, |
409 | { FC_WEIGHT_EXTRABLACK, SkFS::kExtraBlack_Weight }, |
410 | }; |
411 | SkScalar weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR), |
412 | weightRanges, SK_ARRAY_COUNT(weightRanges)); |
413 | |
414 | static constexpr MapRanges widthRanges[] = { |
415 | { FC_WIDTH_ULTRACONDENSED, SkFS::kUltraCondensed_Width }, |
416 | { FC_WIDTH_EXTRACONDENSED, SkFS::kExtraCondensed_Width }, |
417 | { FC_WIDTH_CONDENSED, SkFS::kCondensed_Width }, |
418 | { FC_WIDTH_SEMICONDENSED, SkFS::kSemiCondensed_Width }, |
419 | { FC_WIDTH_NORMAL, SkFS::kNormal_Width }, |
420 | { FC_WIDTH_SEMIEXPANDED, SkFS::kSemiExpanded_Width }, |
421 | { FC_WIDTH_EXPANDED, SkFS::kExpanded_Width }, |
422 | { FC_WIDTH_EXTRAEXPANDED, SkFS::kExtraExpanded_Width }, |
423 | { FC_WIDTH_ULTRAEXPANDED, SkFS::kUltraExpanded_Width }, |
424 | }; |
425 | SkScalar width = map_ranges(get_int(pattern, FC_WIDTH, FC_WIDTH_NORMAL), |
426 | widthRanges, SK_ARRAY_COUNT(widthRanges)); |
427 | |
428 | SkFS::Slant slant = SkFS::kUpright_Slant; |
429 | switch (get_int(pattern, FC_SLANT, FC_SLANT_ROMAN)) { |
430 | case FC_SLANT_ROMAN: slant = SkFS::kUpright_Slant; break; |
431 | case FC_SLANT_ITALIC : slant = SkFS::kItalic_Slant ; break; |
432 | case FC_SLANT_OBLIQUE: slant = SkFS::kOblique_Slant; break; |
433 | default: SkASSERT(false); break; |
434 | } |
435 | |
436 | return SkFontStyle(SkScalarRoundToInt(weight), SkScalarRoundToInt(width), slant); |
437 | } |
438 | |
439 | static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) { |
440 | typedef SkFontStyle SkFS; |
441 | |
442 | static constexpr MapRanges weightRanges[] = { |
443 | { SkFS::kThin_Weight, FC_WEIGHT_THIN }, |
444 | { SkFS::kExtraLight_Weight, FC_WEIGHT_EXTRALIGHT }, |
445 | { SkFS::kLight_Weight, FC_WEIGHT_LIGHT }, |
446 | { 350, FC_WEIGHT_DEMILIGHT }, |
447 | { 380, FC_WEIGHT_BOOK }, |
448 | { SkFS::kNormal_Weight, FC_WEIGHT_REGULAR }, |
449 | { SkFS::kMedium_Weight, FC_WEIGHT_MEDIUM }, |
450 | { SkFS::kSemiBold_Weight, FC_WEIGHT_DEMIBOLD }, |
451 | { SkFS::kBold_Weight, FC_WEIGHT_BOLD }, |
452 | { SkFS::kExtraBold_Weight, FC_WEIGHT_EXTRABOLD }, |
453 | { SkFS::kBlack_Weight, FC_WEIGHT_BLACK }, |
454 | { SkFS::kExtraBlack_Weight, FC_WEIGHT_EXTRABLACK }, |
455 | }; |
456 | int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges)); |
457 | |
458 | static constexpr MapRanges widthRanges[] = { |
459 | { SkFS::kUltraCondensed_Width, FC_WIDTH_ULTRACONDENSED }, |
460 | { SkFS::kExtraCondensed_Width, FC_WIDTH_EXTRACONDENSED }, |
461 | { SkFS::kCondensed_Width, FC_WIDTH_CONDENSED }, |
462 | { SkFS::kSemiCondensed_Width, FC_WIDTH_SEMICONDENSED }, |
463 | { SkFS::kNormal_Width, FC_WIDTH_NORMAL }, |
464 | { SkFS::kSemiExpanded_Width, FC_WIDTH_SEMIEXPANDED }, |
465 | { SkFS::kExpanded_Width, FC_WIDTH_EXPANDED }, |
466 | { SkFS::kExtraExpanded_Width, FC_WIDTH_EXTRAEXPANDED }, |
467 | { SkFS::kUltraExpanded_Width, FC_WIDTH_ULTRAEXPANDED }, |
468 | }; |
469 | int width = map_ranges(style.width(), widthRanges, SK_ARRAY_COUNT(widthRanges)); |
470 | |
471 | int slant = FC_SLANT_ROMAN; |
472 | switch (style.slant()) { |
473 | case SkFS::kUpright_Slant: slant = FC_SLANT_ROMAN ; break; |
474 | case SkFS::kItalic_Slant : slant = FC_SLANT_ITALIC ; break; |
475 | case SkFS::kOblique_Slant: slant = FC_SLANT_OBLIQUE; break; |
476 | default: SkASSERT(false); break; |
477 | } |
478 | |
479 | FcPatternAddInteger(pattern, FC_WEIGHT, weight); |
480 | FcPatternAddInteger(pattern, FC_WIDTH , width); |
481 | FcPatternAddInteger(pattern, FC_SLANT , slant); |
482 | } |
483 | |
484 | } // anonymous namespace |
485 | |
486 | /////////////////////////////////////////////////////////////////////////////// |
487 | |
488 | #define kMaxFontFamilyLength 2048 |
489 | #ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS |
490 | const char* kFontFormatTrueType = "TrueType" ; |
491 | const char* kFontFormatCFF = "CFF" ; |
492 | #endif |
493 | |
494 | SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() { |
495 | SkDEBUGCODE(fontconfiginterface_unittest();) |
496 | } |
497 | |
498 | SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() { |
499 | } |
500 | |
501 | bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) { |
502 | if (access(filename, R_OK) != 0) { |
503 | return false; |
504 | } |
505 | return true; |
506 | } |
507 | |
508 | bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) { |
509 | #ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS |
510 | const char* font_format = get_string(pattern, FC_FONTFORMAT); |
511 | if (font_format |
512 | && strcmp(font_format, kFontFormatTrueType) != 0 |
513 | && strcmp(font_format, kFontFormatCFF) != 0) |
514 | { |
515 | return false; |
516 | } |
517 | #endif |
518 | |
519 | // fontconfig can also return fonts which are unreadable |
520 | const char* c_filename = get_string(pattern, FC_FILE); |
521 | if (!c_filename) { |
522 | return false; |
523 | } |
524 | UniqueFCConfig fcConfig(FcConfigReference(nullptr)); |
525 | const char* sysroot = (const char*)FcConfigGetSysRoot(fcConfig.get()); |
526 | SkString resolvedFilename; |
527 | if (sysroot) { |
528 | resolvedFilename = sysroot; |
529 | resolvedFilename += c_filename; |
530 | c_filename = resolvedFilename.c_str(); |
531 | } |
532 | return this->isAccessible(c_filename); |
533 | } |
534 | |
535 | // Find matching font from |font_set| for the given font family. |
536 | FcPattern* SkFontConfigInterfaceDirect::MatchFont(FcFontSet* font_set, |
537 | const char* post_config_family, |
538 | const SkString& family) { |
539 | // Older versions of fontconfig have a bug where they cannot select |
540 | // only scalable fonts so we have to manually filter the results. |
541 | FcPattern* match = nullptr; |
542 | for (int i = 0; i < font_set->nfont; ++i) { |
543 | FcPattern* current = font_set->fonts[i]; |
544 | if (this->isValidPattern(current)) { |
545 | match = current; |
546 | break; |
547 | } |
548 | } |
549 | |
550 | if (match && !IsFallbackFontAllowed(family)) { |
551 | bool acceptable_substitute = false; |
552 | for (int id = 0; id < 255; ++id) { |
553 | const char* post_match_family = get_string(match, FC_FAMILY, id); |
554 | if (!post_match_family) |
555 | break; |
556 | acceptable_substitute = |
557 | (strcasecmp(post_config_family, post_match_family) == 0 || |
558 | // Workaround for Issue 12530: |
559 | // requested family: "Bitstream Vera Sans" |
560 | // post_config_family: "Arial" |
561 | // post_match_family: "Bitstream Vera Sans" |
562 | // -> We should treat this case as a good match. |
563 | strcasecmp(family.c_str(), post_match_family) == 0) || |
564 | IsMetricCompatibleReplacement(family.c_str(), post_match_family); |
565 | if (acceptable_substitute) |
566 | break; |
567 | } |
568 | if (!acceptable_substitute) |
569 | return nullptr; |
570 | } |
571 | |
572 | return match; |
573 | } |
574 | |
575 | bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[], |
576 | SkFontStyle style, |
577 | FontIdentity* outIdentity, |
578 | SkString* outFamilyName, |
579 | SkFontStyle* outStyle) { |
580 | SkString familyStr(familyName ? familyName : "" ); |
581 | if (familyStr.size() > kMaxFontFamilyLength) { |
582 | return false; |
583 | } |
584 | |
585 | FCLocker lock; |
586 | UniqueFCConfig fcConfig(FcConfigReference(nullptr)); |
587 | FcPattern* pattern = FcPatternCreate(); |
588 | |
589 | if (familyName) { |
590 | FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); |
591 | } |
592 | fcpattern_from_skfontstyle(style, pattern); |
593 | |
594 | FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); |
595 | |
596 | FcConfigSubstitute(fcConfig.get(), pattern, FcMatchPattern); |
597 | FcDefaultSubstitute(pattern); |
598 | |
599 | // Font matching: |
600 | // CSS often specifies a fallback list of families: |
601 | // font-family: a, b, c, serif; |
602 | // However, fontconfig will always do its best to find *a* font when asked |
603 | // for something so we need a way to tell if the match which it has found is |
604 | // "good enough" for us. Otherwise, we can return nullptr which gets piped up |
605 | // and lets WebKit know to try the next CSS family name. However, fontconfig |
606 | // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we |
607 | // wish to support that. |
608 | // |
609 | // Thus, if a specific family is requested we set @family_requested. Then we |
610 | // record two strings: the family name after config processing and the |
611 | // family name after resolving. If the two are equal, it's a good match. |
612 | // |
613 | // So consider the case where a user has mapped Arial to Helvetica in their |
614 | // config. |
615 | // requested family: "Arial" |
616 | // post_config_family: "Helvetica" |
617 | // post_match_family: "Helvetica" |
618 | // -> good match |
619 | // |
620 | // and for a missing font: |
621 | // requested family: "Monaco" |
622 | // post_config_family: "Monaco" |
623 | // post_match_family: "Times New Roman" |
624 | // -> BAD match |
625 | // |
626 | // However, we special-case fallback fonts; see IsFallbackFontAllowed(). |
627 | |
628 | const char* post_config_family = get_string(pattern, FC_FAMILY); |
629 | if (!post_config_family) { |
630 | // we can just continue with an empty name, e.g. default font |
631 | post_config_family = "" ; |
632 | } |
633 | |
634 | FcResult result; |
635 | FcFontSet* font_set = FcFontSort(fcConfig.get(), pattern, 0, nullptr, &result); |
636 | if (!font_set) { |
637 | FcPatternDestroy(pattern); |
638 | return false; |
639 | } |
640 | |
641 | FcPattern* match = this->MatchFont(font_set, post_config_family, familyStr); |
642 | if (!match) { |
643 | FcPatternDestroy(pattern); |
644 | FcFontSetDestroy(font_set); |
645 | return false; |
646 | } |
647 | |
648 | FcPatternDestroy(pattern); |
649 | |
650 | // From here out we just extract our results from 'match' |
651 | |
652 | post_config_family = get_string(match, FC_FAMILY); |
653 | if (!post_config_family) { |
654 | FcFontSetDestroy(font_set); |
655 | return false; |
656 | } |
657 | |
658 | const char* c_filename = get_string(match, FC_FILE); |
659 | if (!c_filename) { |
660 | FcFontSetDestroy(font_set); |
661 | return false; |
662 | } |
663 | const char* sysroot = (const char*)FcConfigGetSysRoot(fcConfig.get()); |
664 | SkString resolvedFilename; |
665 | if (sysroot) { |
666 | resolvedFilename = sysroot; |
667 | resolvedFilename += c_filename; |
668 | c_filename = resolvedFilename.c_str(); |
669 | } |
670 | |
671 | int face_index = get_int(match, FC_INDEX, 0); |
672 | |
673 | FcFontSetDestroy(font_set); |
674 | |
675 | if (outIdentity) { |
676 | outIdentity->fTTCIndex = face_index; |
677 | outIdentity->fString.set(c_filename); |
678 | } |
679 | if (outFamilyName) { |
680 | outFamilyName->set(post_config_family); |
681 | } |
682 | if (outStyle) { |
683 | *outStyle = skfontstyle_from_fcpattern(match); |
684 | } |
685 | return true; |
686 | } |
687 | |
688 | SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) { |
689 | return SkStream::MakeFromFile(identity.fString.c_str()).release(); |
690 | } |
691 | |