2 * Copyright 2009 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 /* migrated from chrome/src/skia/ext/SkFontHost_fontconfig_direct.cpp */
13 #include <fontconfig/fontconfig.h>
16 #include "SkFontConfigInterface.h"
17 #include "SkLazyPtr.h"
21 size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const {
22 size_t size = sizeof(fID) + sizeof(fTTCIndex);
23 size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic
24 size += sizeof(int32_t) + fString.size(); // store length+data
26 SkWBuffer buffer(addr, size);
29 buffer.write32(fTTCIndex);
30 buffer.write32(fString.size());
31 buffer.write32(fStyle.weight());
32 buffer.write32(fStyle.width());
33 buffer.write8(fStyle.slant());
34 buffer.write(fString.c_str(), fString.size());
37 SkASSERT(buffer.pos() == size);
42 size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr,
44 SkRBuffer buffer(addr, size);
46 (void)buffer.readU32(&fID);
47 (void)buffer.readS32(&fTTCIndex);
48 uint32_t strLen, weight, width;
49 (void)buffer.readU32(&strLen);
50 (void)buffer.readU32(&weight);
51 (void)buffer.readU32(&width);
53 (void)buffer.readU8(&u8);
54 SkFontStyle::Slant slant = (SkFontStyle::Slant)u8;
55 fStyle = SkFontStyle(weight, width, slant);
56 fString.resize(strLen);
57 (void)buffer.read(fString.writable_str(), strLen);
58 buffer.skipToAlign4();
60 return buffer.pos(); // the actual number of bytes read
64 static void make_iden(SkFontConfigInterface::FontIdentity* iden) {
67 iden->fString.set("Hello world");
68 iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant);
71 static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0,
73 SkFontConfigInterface::FontIdentity iden1;
75 size_t size0 = iden0.writeToMemory(NULL);
77 SkAutoMalloc storage(size0);
78 memset(storage.get(), initValue, size0);
80 size_t size1 = iden0.writeToMemory(storage.get());
81 SkASSERT(size0 == size1);
83 SkASSERT(iden0 != iden1);
84 size_t size2 = iden1.readFromMemory(storage.get(), size1);
85 SkASSERT(size2 == size1);
86 SkASSERT(iden0 == iden1);
89 static void fontconfiginterface_unittest() {
90 SkFontConfigInterface::FontIdentity iden0, iden1;
92 SkASSERT(iden0 == iden1);
95 SkASSERT(iden0 != iden1);
98 SkASSERT(iden0 == iden1);
100 test_writeToMemory(iden0, 0);
101 test_writeToMemory(iden0, 0);
105 class SkFontConfigInterfaceDirect : public SkFontConfigInterface {
107 SkFontConfigInterfaceDirect();
108 virtual ~SkFontConfigInterfaceDirect();
110 virtual bool matchFamilyName(const char familyName[],
111 SkTypeface::Style requested,
112 FontIdentity* outFontIdentifier,
113 SkString* outFamilyName,
114 SkTypeface::Style* outStyle) SK_OVERRIDE;
115 SkStreamAsset* openStream(const FontIdentity&) SK_OVERRIDE;
118 SkDataTable* getFamilyNames() SK_OVERRIDE;
119 virtual bool matchFamilySet(const char inFamilyName[],
120 SkString* outFamilyName,
121 SkTArray<FontIdentity>*) SK_OVERRIDE;
127 SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface(SkBaseMutex* mutex) {
128 SkAutoMutexAcquire ac(mutex);
129 static SkFontConfigInterfaceDirect* singleton = NULL;
130 if (singleton == NULL) {
131 singleton = SkNEW(SkFontConfigInterfaceDirect);
136 ///////////////////////////////////////////////////////////////////////////////
138 // Returns the string from the pattern, or NULL
139 static const char* get_name(FcPattern* pattern, const char field[],
142 if (FcPatternGetString(pattern, field, index,
143 (FcChar8**)&name) != FcResultMatch) {
149 ///////////////////////////////////////////////////////////////////////////////
153 // Equivalence classes, used to match the Liberation and other fonts
154 // with their metric-compatible replacements. See the discussion in
155 // GetFontEquivClass().
178 // Match the font name against a whilelist of fonts, returning the equivalence
180 FontEquivClass GetFontEquivClass(const char* fontname)
182 // It would be nice for fontconfig to tell us whether a given suggested
183 // replacement is a "strong" match (that is, an equivalent font) or
184 // a "weak" match (that is, fontconfig's next-best attempt at finding a
185 // substitute). However, I played around with the fontconfig API for
186 // a good few hours and could not make it reveal this information.
188 // So instead, we hardcode. Initially this function emulated
189 // /etc/fonts/conf.d/30-metric-aliases.conf
190 // from my Ubuntu system, but we're better off being very conservative.
192 // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
193 // Arial, Times New Roman and Courier New with a character repertoire
194 // much larger than Liberation. Note that Cousine is metrically
195 // compatible with Courier New, but the former is sans-serif while
196 // the latter is serif.
199 struct FontEquivMap {
200 FontEquivClass clazz;
204 static const FontEquivMap kFontEquivMap[] = {
207 { SANS, "Liberation Sans" },
209 { SERIF, "Times New Roman" },
211 { SERIF, "Liberation Serif" },
213 { MONO, "Courier New" },
215 { MONO, "Liberation Mono" },
217 { SYMBOL, "Symbol" },
218 { SYMBOL, "Symbol Neu" },
221 { PGOTHIC, "MS PGothic" },
222 { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
223 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
224 { PGOTHIC, "Noto Sans CJK JP" },
225 { PGOTHIC, "IPAPGothic" },
226 { PGOTHIC, "MotoyaG04Gothic" },
229 { GOTHIC, "MS Gothic" },
230 { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
231 "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
232 { GOTHIC, "IPAGothic" },
233 { GOTHIC, "MotoyaG04GothicMono" },
236 { PMINCHO, "MS PMincho" },
237 { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
238 "\xe6\x98\x8e\xe6\x9c\x9d"},
239 { PMINCHO, "IPAPMincho" },
240 { PMINCHO, "MotoyaG04Mincho" },
243 { MINCHO, "MS Mincho" },
244 { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
245 { MINCHO, "IPAMincho" },
246 { MINCHO, "MotoyaG04MinchoMono" },
249 { SIMSUN, "Simsun" },
250 { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
251 { SIMSUN, "MSung GB18030" },
252 { SIMSUN, "Song ASC" },
255 { NSIMSUN, "NSimsun" },
256 { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
257 { NSIMSUN, "MSung GB18030" },
258 { NSIMSUN, "N Song ASC" },
261 { SIMHEI, "Simhei" },
262 { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
263 { SIMHEI, "Noto Sans CJK SC" },
264 { SIMHEI, "MYingHeiGB18030" },
265 { SIMHEI, "MYingHeiB5HK" },
268 { PMINGLIU, "PMingLiU"},
269 { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
270 { PMINGLIU, "MSung B5HK"},
273 { MINGLIU, "MingLiU"},
274 { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" },
275 { MINGLIU, "MSung B5HK"},
278 { PMINGLIUHK, "PMingLiU_HKSCS"},
279 { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
280 { PMINGLIUHK, "MSung B5HK"},
283 { MINGLIUHK, "MingLiU_HKSCS"},
284 { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" },
285 { MINGLIUHK, "MSung B5HK"},
288 { CAMBRIA, "Cambria" },
289 { CAMBRIA, "Caladea" },
292 { CALIBRI, "Calibri" },
293 { CALIBRI, "Carlito" },
296 static const size_t kFontCount =
297 sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
299 // TODO(jungshik): If this loop turns out to be hot, turn
300 // the array to a static (hash)map to speed it up.
301 for (size_t i = 0; i < kFontCount; ++i) {
302 if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
303 return kFontEquivMap[i].clazz;
309 // Return true if |font_a| and |font_b| are visually and at the metrics
310 // level interchangeable.
311 bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
313 FontEquivClass class_a = GetFontEquivClass(font_a);
314 FontEquivClass class_b = GetFontEquivClass(font_b);
316 return class_a != OTHER && class_a == class_b;
319 // Normally we only return exactly the font asked for. In last-resort
320 // cases, the request either doesn't specify a font or is one of the
321 // basic font names like "Sans", "Serif" or "Monospace". This function
322 // tells you whether a given request is for such a fallback.
323 bool IsFallbackFontAllowed(const SkString& family) {
324 const char* family_cstr = family.c_str();
325 return family.isEmpty() ||
326 strcasecmp(family_cstr, "sans") == 0 ||
327 strcasecmp(family_cstr, "serif") == 0 ||
328 strcasecmp(family_cstr, "monospace") == 0;
331 static bool valid_pattern(FcPattern* pattern) {
332 #ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
334 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
340 // fontconfig can also return fonts which are unreadable
341 const char* c_filename = get_name(pattern, FC_FILE);
345 if (access(c_filename, R_OK) != 0) {
351 // Find matching font from |font_set| for the given font family.
352 FcPattern* MatchFont(FcFontSet* font_set,
353 const char* post_config_family,
354 const SkString& family) {
355 // Older versions of fontconfig have a bug where they cannot select
356 // only scalable fonts so we have to manually filter the results.
357 FcPattern* match = NULL;
358 for (int i = 0; i < font_set->nfont; ++i) {
359 FcPattern* current = font_set->fonts[i];
360 if (valid_pattern(current)) {
366 if (match && !IsFallbackFontAllowed(family)) {
367 bool acceptable_substitute = false;
368 for (int id = 0; id < 255; ++id) {
369 const char* post_match_family = get_name(match, FC_FAMILY, id);
370 if (!post_match_family)
372 acceptable_substitute =
373 (strcasecmp(post_config_family, post_match_family) == 0 ||
374 // Workaround for Issue 12530:
375 // requested family: "Bitstream Vera Sans"
376 // post_config_family: "Arial"
377 // post_match_family: "Bitstream Vera Sans"
378 // -> We should treat this case as a good match.
379 strcasecmp(family.c_str(), post_match_family) == 0) ||
380 IsMetricCompatibleReplacement(family.c_str(), post_match_family);
381 if (acceptable_substitute)
384 if (!acceptable_substitute)
391 // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
392 SkTypeface::Style GetFontStyle(FcPattern* font) {
394 if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
395 resulting_bold = FC_WEIGHT_NORMAL;
397 int resulting_italic;
398 if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
399 resulting_italic = FC_SLANT_ROMAN;
401 // If we ask for an italic font, fontconfig might take a roman font and set
402 // the undocumented property FC_MATRIX to a skew matrix. It'll then say
403 // that the font is italic or oblique. So, if we see a matrix, we don't
404 // believe that it's italic.
406 const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
408 // If we ask for an italic font, fontconfig might take a roman font and set
411 const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
414 if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) {
415 styleBits |= SkTypeface::kBold;
417 if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) {
418 styleBits |= SkTypeface::kItalic;
421 return (SkTypeface::Style)styleBits;
424 } // anonymous namespace
426 ///////////////////////////////////////////////////////////////////////////////
428 #define kMaxFontFamilyLength 2048
430 SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() {
431 SkAutoMutexAcquire ac(mutex_);
435 SkDEBUGCODE(fontconfiginterface_unittest();)
438 SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() {
441 bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[],
442 SkTypeface::Style style,
443 FontIdentity* outIdentity,
444 SkString* outFamilyName,
445 SkTypeface::Style* outStyle) {
446 SkString familyStr(familyName ? familyName : "");
447 if (familyStr.size() > kMaxFontFamilyLength) {
451 SkAutoMutexAcquire ac(mutex_);
453 FcPattern* pattern = FcPatternCreate();
456 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
458 FcPatternAddInteger(pattern, FC_WEIGHT,
459 (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD
461 FcPatternAddInteger(pattern, FC_SLANT,
462 (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC
464 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
466 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
467 FcDefaultSubstitute(pattern);
470 // CSS often specifies a fallback list of families:
471 // font-family: a, b, c, serif;
472 // However, fontconfig will always do its best to find *a* font when asked
473 // for something so we need a way to tell if the match which it has found is
474 // "good enough" for us. Otherwise, we can return NULL which gets piped up
475 // and lets WebKit know to try the next CSS family name. However, fontconfig
476 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
477 // wish to support that.
479 // Thus, if a specific family is requested we set @family_requested. Then we
480 // record two strings: the family name after config processing and the
481 // family name after resolving. If the two are equal, it's a good match.
483 // So consider the case where a user has mapped Arial to Helvetica in their
485 // requested family: "Arial"
486 // post_config_family: "Helvetica"
487 // post_match_family: "Helvetica"
490 // and for a missing font:
491 // requested family: "Monaco"
492 // post_config_family: "Monaco"
493 // post_match_family: "Times New Roman"
496 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
498 const char* post_config_family = get_name(pattern, FC_FAMILY);
499 if (!post_config_family) {
500 // we can just continue with an empty name, e.g. default font
501 post_config_family = "";
505 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
507 FcPatternDestroy(pattern);
511 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
513 FcPatternDestroy(pattern);
514 FcFontSetDestroy(font_set);
518 FcPatternDestroy(pattern);
520 // From here out we just extract our results from 'match'
522 post_config_family = get_name(match, FC_FAMILY);
523 if (!post_config_family) {
524 FcFontSetDestroy(font_set);
528 const char* c_filename = get_name(match, FC_FILE);
530 FcFontSetDestroy(font_set);
535 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
536 FcFontSetDestroy(font_set);
540 FcFontSetDestroy(font_set);
543 outIdentity->fTTCIndex = face_index;
544 outIdentity->fString.set(c_filename);
547 outFamilyName->set(post_config_family);
550 *outStyle = GetFontStyle(match);
555 SkStreamAsset* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) {
556 return SkStream::NewFromFile(identity.fString.c_str());
559 ///////////////////////////////////////////////////////////////////////////////
561 static bool find_name(const SkTDArray<const char*>& list, const char* str) {
562 int count = list.count();
563 for (int i = 0; i < count; ++i) {
564 if (!strcmp(list[i], str)) {
571 SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() {
572 SkAutoMutexAcquire ac(mutex_);
574 FcPattern* pat = FcPatternCreate();
575 SkAutoTCallVProc<FcPattern, FcPatternDestroy> autoDestroyPat(pat);
580 FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0);
581 SkAutoTCallVProc<FcObjectSet, FcObjectSetDestroy> autoDestroyOs(os);
586 FcFontSet* fs = FcFontList(NULL, pat, os);
587 SkAutoTCallVProc<FcFontSet, FcFontSetDestroy> autoDestroyFs(fs);
592 SkTDArray<const char*> names;
593 SkTDArray<size_t> sizes;
594 for (int i = 0; i < fs->nfont; ++i) {
595 FcPattern* match = fs->fonts[i];
596 const char* famName = get_name(match, FC_FAMILY);
597 if (famName && !find_name(names, famName)) {
598 *names.append() = famName;
599 *sizes.append() = strlen(famName) + 1;
603 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
604 sizes.begin(), names.count());
607 bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[],
608 SkString* outFamilyName,
609 SkTArray<FontIdentity>* ids) {
610 SkAutoMutexAcquire ac(mutex_);
613 SkString familyStr(familyName ? familyName : "");
614 if (familyStr.size() > kMaxFontFamilyLength) {
618 SkAutoMutexAcquire ac(mutex_);
620 FcPattern* pattern = FcPatternCreate();
623 FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
625 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
627 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
628 FcDefaultSubstitute(pattern);
631 // CSS often specifies a fallback list of families:
632 // font-family: a, b, c, serif;
633 // However, fontconfig will always do its best to find *a* font when asked
634 // for something so we need a way to tell if the match which it has found is
635 // "good enough" for us. Otherwise, we can return NULL which gets piped up
636 // and lets WebKit know to try the next CSS family name. However, fontconfig
637 // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
638 // wish to support that.
640 // Thus, if a specific family is requested we set @family_requested. Then we
641 // record two strings: the family name after config processing and the
642 // family name after resolving. If the two are equal, it's a good match.
644 // So consider the case where a user has mapped Arial to Helvetica in their
646 // requested family: "Arial"
647 // post_config_family: "Helvetica"
648 // post_match_family: "Helvetica"
651 // and for a missing font:
652 // requested family: "Monaco"
653 // post_config_family: "Monaco"
654 // post_match_family: "Times New Roman"
657 // However, we special-case fallback fonts; see IsFallbackFontAllowed().
659 const char* post_config_family = get_name(pattern, FC_FAMILY);
662 FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
664 FcPatternDestroy(pattern);
668 FcPattern* match = MatchFont(font_set, post_config_family, familyStr);
670 FcPatternDestroy(pattern);
671 FcFontSetDestroy(font_set);
675 FcPatternDestroy(pattern);
677 // From here out we just extract our results from 'match'
679 if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) {
680 FcFontSetDestroy(font_set);
685 if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
686 FcFontSetDestroy(font_set);
691 if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
692 FcFontSetDestroy(font_set);
696 FcFontSetDestroy(font_set);
699 outIdentity->fTTCIndex = face_index;
700 outIdentity->fString.set((const char*)c_filename);
703 outFamilyName->set((const char*)post_config_family);
706 *outStyle = GetFontStyle(match);
713 FcPattern** match = MatchFont(font_set, post_config_family, &count);
715 FcPatternDestroy(pattern);
716 FcFontSetDestroy(font_set);
720 FcPatternDestroy(pattern);
722 SkTDArray<FcPattern*> trimmedMatches;
723 for (int i = 0; i < count; ++i) {
724 const char* justName = find_just_name(get_name(match[i], FC_FILE));
725 if (!is_lower(*justName)) {
726 *trimmedMatches.append() = match[i];
730 SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
731 (trimmedMatches.begin(),
732 trimmedMatches.count()));