| 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 |  | 
|---|