michael@0: /* michael@0: * Copyright 2009 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: /* migrated from chrome/src/skia/ext/SkFontHost_fontconfig_direct.cpp */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: #include "SkBuffer.h" michael@0: #include "SkFontConfigInterface.h" michael@0: #include "SkStream.h" michael@0: michael@0: size_t SkFontConfigInterface::FontIdentity::writeToMemory(void* addr) const { michael@0: size_t size = sizeof(fID) + sizeof(fTTCIndex); michael@0: size += sizeof(int32_t) + sizeof(int32_t) + sizeof(uint8_t); // weight, width, italic michael@0: size += sizeof(int32_t) + fString.size(); // store length+data michael@0: if (addr) { michael@0: SkWBuffer buffer(addr, size); michael@0: michael@0: buffer.write32(fID); michael@0: buffer.write32(fTTCIndex); michael@0: buffer.write32(fString.size()); michael@0: buffer.write32(fStyle.weight()); michael@0: buffer.write32(fStyle.width()); michael@0: buffer.write8(fStyle.slant()); michael@0: buffer.write(fString.c_str(), fString.size()); michael@0: buffer.padToAlign4(); michael@0: michael@0: SkASSERT(buffer.pos() == size); michael@0: } michael@0: return size; michael@0: } michael@0: michael@0: size_t SkFontConfigInterface::FontIdentity::readFromMemory(const void* addr, michael@0: size_t size) { michael@0: SkRBuffer buffer(addr, size); michael@0: michael@0: (void)buffer.readU32(&fID); michael@0: (void)buffer.readS32(&fTTCIndex); michael@0: uint32_t strLen, weight, width; michael@0: (void)buffer.readU32(&strLen); michael@0: (void)buffer.readU32(&weight); michael@0: (void)buffer.readU32(&width); michael@0: uint8_t u8; michael@0: (void)buffer.readU8(&u8); michael@0: SkFontStyle::Slant slant = (SkFontStyle::Slant)u8; michael@0: fStyle = SkFontStyle(weight, width, slant); michael@0: fString.resize(strLen); michael@0: (void)buffer.read(fString.writable_str(), strLen); michael@0: buffer.skipToAlign4(); michael@0: michael@0: return buffer.pos(); // the actual number of bytes read michael@0: } michael@0: michael@0: #ifdef SK_DEBUG michael@0: static void make_iden(SkFontConfigInterface::FontIdentity* iden) { michael@0: iden->fID = 10; michael@0: iden->fTTCIndex = 2; michael@0: iden->fString.set("Hello world"); michael@0: iden->fStyle = SkFontStyle(300, 6, SkFontStyle::kItalic_Slant); michael@0: } michael@0: michael@0: static void test_writeToMemory(const SkFontConfigInterface::FontIdentity& iden0, michael@0: int initValue) { michael@0: SkFontConfigInterface::FontIdentity iden1; michael@0: michael@0: size_t size0 = iden0.writeToMemory(NULL); michael@0: michael@0: SkAutoMalloc storage(size0); michael@0: memset(storage.get(), initValue, size0); michael@0: michael@0: size_t size1 = iden0.writeToMemory(storage.get()); michael@0: SkASSERT(size0 == size1); michael@0: michael@0: SkASSERT(iden0 != iden1); michael@0: size_t size2 = iden1.readFromMemory(storage.get(), size1); michael@0: SkASSERT(size2 == size1); michael@0: SkASSERT(iden0 == iden1); michael@0: } michael@0: michael@0: static void fontconfiginterface_unittest() { michael@0: SkFontConfigInterface::FontIdentity iden0, iden1; michael@0: michael@0: SkASSERT(iden0 == iden1); michael@0: michael@0: make_iden(&iden0); michael@0: SkASSERT(iden0 != iden1); michael@0: michael@0: make_iden(&iden1); michael@0: SkASSERT(iden0 == iden1); michael@0: michael@0: test_writeToMemory(iden0, 0); michael@0: test_writeToMemory(iden0, 0); michael@0: } michael@0: #endif michael@0: michael@0: class SkFontConfigInterfaceDirect : public SkFontConfigInterface { michael@0: public: michael@0: SkFontConfigInterfaceDirect(); michael@0: virtual ~SkFontConfigInterfaceDirect(); michael@0: michael@0: virtual bool matchFamilyName(const char familyName[], michael@0: SkTypeface::Style requested, michael@0: FontIdentity* outFontIdentifier, michael@0: SkString* outFamilyName, michael@0: SkTypeface::Style* outStyle) SK_OVERRIDE; michael@0: virtual SkStream* openStream(const FontIdentity&) SK_OVERRIDE; michael@0: michael@0: // new APIs michael@0: virtual SkDataTable* getFamilyNames() SK_OVERRIDE; michael@0: virtual bool matchFamilySet(const char inFamilyName[], michael@0: SkString* outFamilyName, michael@0: SkTArray*) SK_OVERRIDE; michael@0: michael@0: private: michael@0: SkMutex mutex_; michael@0: }; michael@0: michael@0: SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface() { michael@0: static SkFontConfigInterface* gDirect; michael@0: if (NULL == gDirect) { michael@0: static SkMutex gMutex; michael@0: SkAutoMutexAcquire ac(gMutex); michael@0: michael@0: if (NULL == gDirect) { michael@0: gDirect = new SkFontConfigInterfaceDirect; michael@0: } michael@0: } michael@0: return gDirect; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // Returns the string from the pattern, or NULL michael@0: static const char* get_name(FcPattern* pattern, const char field[], michael@0: int index = 0) { michael@0: const char* name; michael@0: if (FcPatternGetString(pattern, field, index, michael@0: (FcChar8**)&name) != FcResultMatch) { michael@0: name = NULL; michael@0: } michael@0: return name; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: namespace { michael@0: michael@0: // Equivalence classes, used to match the Liberation and other fonts michael@0: // with their metric-compatible replacements. See the discussion in michael@0: // GetFontEquivClass(). michael@0: enum FontEquivClass michael@0: { michael@0: OTHER, michael@0: SANS, michael@0: SERIF, michael@0: MONO, michael@0: SYMBOL, michael@0: PGOTHIC, michael@0: GOTHIC, michael@0: PMINCHO, michael@0: MINCHO, michael@0: SIMSUN, michael@0: NSIMSUN, michael@0: SIMHEI, michael@0: PMINGLIU, michael@0: MINGLIU, michael@0: PMINGLIUHK, michael@0: MINGLIUHK, michael@0: CAMBRIA, michael@0: CALIBRI, michael@0: }; michael@0: michael@0: // Match the font name against a whilelist of fonts, returning the equivalence michael@0: // class. michael@0: FontEquivClass GetFontEquivClass(const char* fontname) michael@0: { michael@0: // It would be nice for fontconfig to tell us whether a given suggested michael@0: // replacement is a "strong" match (that is, an equivalent font) or michael@0: // a "weak" match (that is, fontconfig's next-best attempt at finding a michael@0: // substitute). However, I played around with the fontconfig API for michael@0: // a good few hours and could not make it reveal this information. michael@0: // michael@0: // So instead, we hardcode. Initially this function emulated michael@0: // /etc/fonts/conf.d/30-metric-aliases.conf michael@0: // from my Ubuntu system, but we're better off being very conservative. michael@0: michael@0: // Arimo, Tinos and Cousine are a set of fonts metric-compatible with michael@0: // Arial, Times New Roman and Courier New with a character repertoire michael@0: // much larger than Liberation. Note that Cousine is metrically michael@0: // compatible with Courier New, but the former is sans-serif while michael@0: // the latter is serif. michael@0: michael@0: michael@0: struct FontEquivMap { michael@0: FontEquivClass clazz; michael@0: const char name[40]; michael@0: }; michael@0: michael@0: static const FontEquivMap kFontEquivMap[] = { michael@0: { SANS, "Arial" }, michael@0: { SANS, "Arimo" }, michael@0: { SANS, "Liberation Sans" }, michael@0: michael@0: { SERIF, "Times New Roman" }, michael@0: { SERIF, "Tinos" }, michael@0: { SERIF, "Liberation Serif" }, michael@0: michael@0: { MONO, "Courier New" }, michael@0: { MONO, "Cousine" }, michael@0: { MONO, "Liberation Mono" }, michael@0: michael@0: { SYMBOL, "Symbol" }, michael@0: { SYMBOL, "Symbol Neu" }, michael@0: michael@0: // MS Pゴシック michael@0: { PGOTHIC, "MS PGothic" }, michael@0: { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" michael@0: "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, michael@0: { PGOTHIC, "IPAPGothic" }, michael@0: { PGOTHIC, "MotoyaG04Gothic" }, michael@0: michael@0: // MS ゴシック michael@0: { GOTHIC, "MS Gothic" }, michael@0: { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 " michael@0: "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" }, michael@0: { GOTHIC, "IPAGothic" }, michael@0: { GOTHIC, "MotoyaG04GothicMono" }, michael@0: michael@0: // MS P明朝 michael@0: { PMINCHO, "MS PMincho" }, michael@0: { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0" michael@0: "\xe6\x98\x8e\xe6\x9c\x9d"}, michael@0: { PMINCHO, "IPAPMincho" }, michael@0: { PMINCHO, "MotoyaG04Mincho" }, michael@0: michael@0: // MS 明朝 michael@0: { MINCHO, "MS Mincho" }, michael@0: { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" }, michael@0: { MINCHO, "IPAMincho" }, michael@0: { MINCHO, "MotoyaG04MinchoMono" }, michael@0: michael@0: // 宋体 michael@0: { SIMSUN, "Simsun" }, michael@0: { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" }, michael@0: { SIMSUN, "MSung GB18030" }, michael@0: { SIMSUN, "Song ASC" }, michael@0: michael@0: // 新宋体 michael@0: { NSIMSUN, "NSimsun" }, michael@0: { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" }, michael@0: { NSIMSUN, "MSung GB18030" }, michael@0: { NSIMSUN, "N Song ASC" }, michael@0: michael@0: // 黑体 michael@0: { SIMHEI, "Simhei" }, michael@0: { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" }, michael@0: { SIMHEI, "MYingHeiGB18030" }, michael@0: { SIMHEI, "MYingHeiB5HK" }, michael@0: michael@0: // 新細明體 michael@0: { PMINGLIU, "PMingLiU"}, michael@0: { PMINGLIU, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, michael@0: { PMINGLIU, "MSung B5HK"}, michael@0: michael@0: // 細明體 michael@0: { MINGLIU, "MingLiU"}, michael@0: { MINGLIU, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94" }, michael@0: { MINGLIU, "MSung B5HK"}, michael@0: michael@0: // 新細明體 michael@0: { PMINGLIUHK, "PMingLiU_HKSCS"}, michael@0: { PMINGLIUHK, "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, michael@0: { PMINGLIUHK, "MSung B5HK"}, michael@0: michael@0: // 細明體 michael@0: { MINGLIUHK, "MingLiU_HKSCS"}, michael@0: { MINGLIUHK, "\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94_HKSCS" }, michael@0: { MINGLIUHK, "MSung B5HK"}, michael@0: michael@0: // Cambria michael@0: { CAMBRIA, "Cambria" }, michael@0: { CAMBRIA, "Caladea" }, michael@0: michael@0: // Calibri michael@0: { CALIBRI, "Calibri" }, michael@0: { CALIBRI, "Carlito" }, michael@0: }; michael@0: michael@0: static const size_t kFontCount = michael@0: sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]); michael@0: michael@0: // TODO(jungshik): If this loop turns out to be hot, turn michael@0: // the array to a static (hash)map to speed it up. michael@0: for (size_t i = 0; i < kFontCount; ++i) { michael@0: if (strcasecmp(kFontEquivMap[i].name, fontname) == 0) michael@0: return kFontEquivMap[i].clazz; michael@0: } michael@0: return OTHER; michael@0: } michael@0: michael@0: michael@0: // Return true if |font_a| and |font_b| are visually and at the metrics michael@0: // level interchangeable. michael@0: bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b) michael@0: { michael@0: FontEquivClass class_a = GetFontEquivClass(font_a); michael@0: FontEquivClass class_b = GetFontEquivClass(font_b); michael@0: michael@0: return class_a != OTHER && class_a == class_b; michael@0: } michael@0: michael@0: // Normally we only return exactly the font asked for. In last-resort michael@0: // cases, the request either doesn't specify a font or is one of the michael@0: // basic font names like "Sans", "Serif" or "Monospace". This function michael@0: // tells you whether a given request is for such a fallback. michael@0: bool IsFallbackFontAllowed(const std::string& family) { michael@0: const char* family_cstr = family.c_str(); michael@0: return family.empty() || michael@0: strcasecmp(family_cstr, "sans") == 0 || michael@0: strcasecmp(family_cstr, "serif") == 0 || michael@0: strcasecmp(family_cstr, "monospace") == 0; michael@0: } michael@0: michael@0: static bool valid_pattern(FcPattern* pattern) { michael@0: #ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS michael@0: FcBool is_scalable; michael@0: if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch michael@0: || !is_scalable) { michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: // fontconfig can also return fonts which are unreadable michael@0: const char* c_filename = get_name(pattern, FC_FILE); michael@0: if (!c_filename) { michael@0: return false; michael@0: } michael@0: if (access(c_filename, R_OK) != 0) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // Find matching font from |font_set| for the given font family. michael@0: FcPattern* MatchFont(FcFontSet* font_set, michael@0: const char* post_config_family, michael@0: const std::string& family) { michael@0: // Older versions of fontconfig have a bug where they cannot select michael@0: // only scalable fonts so we have to manually filter the results. michael@0: FcPattern* match = NULL; michael@0: for (int i = 0; i < font_set->nfont; ++i) { michael@0: FcPattern* current = font_set->fonts[i]; michael@0: if (valid_pattern(current)) { michael@0: match = current; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (match && !IsFallbackFontAllowed(family)) { michael@0: bool acceptable_substitute = false; michael@0: for (int id = 0; id < 255; ++id) { michael@0: const char* post_match_family = get_name(match, FC_FAMILY, id); michael@0: if (!post_match_family) michael@0: break; michael@0: acceptable_substitute = michael@0: (strcasecmp(post_config_family, post_match_family) == 0 || michael@0: // Workaround for Issue 12530: michael@0: // requested family: "Bitstream Vera Sans" michael@0: // post_config_family: "Arial" michael@0: // post_match_family: "Bitstream Vera Sans" michael@0: // -> We should treat this case as a good match. michael@0: strcasecmp(family.c_str(), post_match_family) == 0) || michael@0: IsMetricCompatibleReplacement(family.c_str(), post_match_family); michael@0: if (acceptable_substitute) michael@0: break; michael@0: } michael@0: if (!acceptable_substitute) michael@0: return NULL; michael@0: } michael@0: michael@0: return match; michael@0: } michael@0: michael@0: // Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|. michael@0: SkTypeface::Style GetFontStyle(FcPattern* font) { michael@0: int resulting_bold; michael@0: if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold)) michael@0: resulting_bold = FC_WEIGHT_NORMAL; michael@0: michael@0: int resulting_italic; michael@0: if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic)) michael@0: resulting_italic = FC_SLANT_ROMAN; michael@0: michael@0: // If we ask for an italic font, fontconfig might take a roman font and set michael@0: // the undocumented property FC_MATRIX to a skew matrix. It'll then say michael@0: // that the font is italic or oblique. So, if we see a matrix, we don't michael@0: // believe that it's italic. michael@0: FcValue matrix; michael@0: const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0; michael@0: michael@0: // If we ask for an italic font, fontconfig might take a roman font and set michael@0: // FC_EMBOLDEN. michael@0: FcValue embolden; michael@0: const bool have_embolden = FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0; michael@0: michael@0: int styleBits = 0; michael@0: if (resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden) { michael@0: styleBits |= SkTypeface::kBold; michael@0: } michael@0: if (resulting_italic > FC_SLANT_ROMAN && !have_matrix) { michael@0: styleBits |= SkTypeface::kItalic; michael@0: } michael@0: michael@0: return (SkTypeface::Style)styleBits; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #define kMaxFontFamilyLength 2048 michael@0: michael@0: SkFontConfigInterfaceDirect::SkFontConfigInterfaceDirect() { michael@0: SkAutoMutexAcquire ac(mutex_); michael@0: michael@0: FcInit(); michael@0: michael@0: SkDEBUGCODE(fontconfiginterface_unittest();) michael@0: } michael@0: michael@0: SkFontConfigInterfaceDirect::~SkFontConfigInterfaceDirect() { michael@0: } michael@0: michael@0: bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[], michael@0: SkTypeface::Style style, michael@0: FontIdentity* outIdentity, michael@0: SkString* outFamilyName, michael@0: SkTypeface::Style* outStyle) { michael@0: std::string familyStr(familyName ? familyName : ""); michael@0: if (familyStr.length() > kMaxFontFamilyLength) { michael@0: return false; michael@0: } michael@0: michael@0: SkAutoMutexAcquire ac(mutex_); michael@0: michael@0: FcPattern* pattern = FcPatternCreate(); michael@0: michael@0: if (familyName) { michael@0: FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); michael@0: } michael@0: FcPatternAddInteger(pattern, FC_WEIGHT, michael@0: (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD michael@0: : FC_WEIGHT_NORMAL); michael@0: FcPatternAddInteger(pattern, FC_SLANT, michael@0: (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC michael@0: : FC_SLANT_ROMAN); michael@0: FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); michael@0: michael@0: FcConfigSubstitute(NULL, pattern, FcMatchPattern); michael@0: FcDefaultSubstitute(pattern); michael@0: michael@0: // Font matching: michael@0: // CSS often specifies a fallback list of families: michael@0: // font-family: a, b, c, serif; michael@0: // However, fontconfig will always do its best to find *a* font when asked michael@0: // for something so we need a way to tell if the match which it has found is michael@0: // "good enough" for us. Otherwise, we can return NULL which gets piped up michael@0: // and lets WebKit know to try the next CSS family name. However, fontconfig michael@0: // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we michael@0: // wish to support that. michael@0: // michael@0: // Thus, if a specific family is requested we set @family_requested. Then we michael@0: // record two strings: the family name after config processing and the michael@0: // family name after resolving. If the two are equal, it's a good match. michael@0: // michael@0: // So consider the case where a user has mapped Arial to Helvetica in their michael@0: // config. michael@0: // requested family: "Arial" michael@0: // post_config_family: "Helvetica" michael@0: // post_match_family: "Helvetica" michael@0: // -> good match michael@0: // michael@0: // and for a missing font: michael@0: // requested family: "Monaco" michael@0: // post_config_family: "Monaco" michael@0: // post_match_family: "Times New Roman" michael@0: // -> BAD match michael@0: // michael@0: // However, we special-case fallback fonts; see IsFallbackFontAllowed(). michael@0: michael@0: const char* post_config_family = get_name(pattern, FC_FAMILY); michael@0: if (!post_config_family) { michael@0: // we can just continue with an empty name, e.g. default font michael@0: post_config_family = ""; michael@0: } michael@0: michael@0: FcResult result; michael@0: FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); michael@0: if (!font_set) { michael@0: FcPatternDestroy(pattern); michael@0: return false; michael@0: } michael@0: michael@0: FcPattern* match = MatchFont(font_set, post_config_family, familyStr); michael@0: if (!match) { michael@0: FcPatternDestroy(pattern); michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: FcPatternDestroy(pattern); michael@0: michael@0: // From here out we just extract our results from 'match' michael@0: michael@0: post_config_family = get_name(match, FC_FAMILY); michael@0: if (!post_config_family) { michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: const char* c_filename = get_name(match, FC_FILE); michael@0: if (!c_filename) { michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: int face_index; michael@0: if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: FcFontSetDestroy(font_set); michael@0: michael@0: if (outIdentity) { michael@0: outIdentity->fTTCIndex = face_index; michael@0: outIdentity->fString.set(c_filename); michael@0: } michael@0: if (outFamilyName) { michael@0: outFamilyName->set(post_config_family); michael@0: } michael@0: if (outStyle) { michael@0: *outStyle = GetFontStyle(match); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SkStream* SkFontConfigInterfaceDirect::openStream(const FontIdentity& identity) { michael@0: return SkStream::NewFromFile(identity.fString.c_str()); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static bool find_name(const SkTDArray& list, const char* str) { michael@0: int count = list.count(); michael@0: for (int i = 0; i < count; ++i) { michael@0: if (!strcmp(list[i], str)) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: SkDataTable* SkFontConfigInterfaceDirect::getFamilyNames() { michael@0: SkAutoMutexAcquire ac(mutex_); michael@0: michael@0: FcPattern* pat = FcPatternCreate(); michael@0: SkAutoTCallVProc autoDestroyPat(pat); michael@0: if (NULL == pat) { michael@0: return NULL; michael@0: } michael@0: michael@0: FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, (char *)0); michael@0: SkAutoTCallVProc autoDestroyOs(os); michael@0: if (NULL == os) { michael@0: return NULL; michael@0: } michael@0: michael@0: FcFontSet* fs = FcFontList(NULL, pat, os); michael@0: SkAutoTCallVProc autoDestroyFs(fs); michael@0: if (NULL == fs) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkTDArray names; michael@0: SkTDArray sizes; michael@0: for (int i = 0; i < fs->nfont; ++i) { michael@0: FcPattern* match = fs->fonts[i]; michael@0: const char* famName = get_name(match, FC_FAMILY); michael@0: if (famName && !find_name(names, famName)) { michael@0: *names.append() = famName; michael@0: *sizes.append() = strlen(famName) + 1; michael@0: } michael@0: } michael@0: michael@0: return SkDataTable::NewCopyArrays((const void*const*)names.begin(), michael@0: sizes.begin(), names.count()); michael@0: } michael@0: michael@0: bool SkFontConfigInterfaceDirect::matchFamilySet(const char inFamilyName[], michael@0: SkString* outFamilyName, michael@0: SkTArray* ids) { michael@0: SkAutoMutexAcquire ac(mutex_); michael@0: michael@0: #if 0 michael@0: std::string familyStr(familyName ? familyName : ""); michael@0: if (familyStr.length() > kMaxFontFamilyLength) { michael@0: return false; michael@0: } michael@0: michael@0: SkAutoMutexAcquire ac(mutex_); michael@0: michael@0: FcPattern* pattern = FcPatternCreate(); michael@0: michael@0: if (familyName) { michael@0: FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); michael@0: } michael@0: FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); michael@0: michael@0: FcConfigSubstitute(NULL, pattern, FcMatchPattern); michael@0: FcDefaultSubstitute(pattern); michael@0: michael@0: // Font matching: michael@0: // CSS often specifies a fallback list of families: michael@0: // font-family: a, b, c, serif; michael@0: // However, fontconfig will always do its best to find *a* font when asked michael@0: // for something so we need a way to tell if the match which it has found is michael@0: // "good enough" for us. Otherwise, we can return NULL which gets piped up michael@0: // and lets WebKit know to try the next CSS family name. However, fontconfig michael@0: // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we michael@0: // wish to support that. michael@0: // michael@0: // Thus, if a specific family is requested we set @family_requested. Then we michael@0: // record two strings: the family name after config processing and the michael@0: // family name after resolving. If the two are equal, it's a good match. michael@0: // michael@0: // So consider the case where a user has mapped Arial to Helvetica in their michael@0: // config. michael@0: // requested family: "Arial" michael@0: // post_config_family: "Helvetica" michael@0: // post_match_family: "Helvetica" michael@0: // -> good match michael@0: // michael@0: // and for a missing font: michael@0: // requested family: "Monaco" michael@0: // post_config_family: "Monaco" michael@0: // post_match_family: "Times New Roman" michael@0: // -> BAD match michael@0: // michael@0: // However, we special-case fallback fonts; see IsFallbackFontAllowed(). michael@0: michael@0: const char* post_config_family = get_name(pattern, FC_FAMILY); michael@0: michael@0: FcResult result; michael@0: FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); michael@0: if (!font_set) { michael@0: FcPatternDestroy(pattern); michael@0: return false; michael@0: } michael@0: michael@0: FcPattern* match = MatchFont(font_set, post_config_family, familyStr); michael@0: if (!match) { michael@0: FcPatternDestroy(pattern); michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: FcPatternDestroy(pattern); michael@0: michael@0: // From here out we just extract our results from 'match' michael@0: michael@0: if (FcPatternGetString(match, FC_FAMILY, 0, &post_config_family) != FcResultMatch) { michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: FcChar8* c_filename; michael@0: if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) { michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: int face_index; michael@0: if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { michael@0: FcFontSetDestroy(font_set); michael@0: return false; michael@0: } michael@0: michael@0: FcFontSetDestroy(font_set); michael@0: michael@0: if (outIdentity) { michael@0: outIdentity->fTTCIndex = face_index; michael@0: outIdentity->fString.set((const char*)c_filename); michael@0: } michael@0: if (outFamilyName) { michael@0: outFamilyName->set((const char*)post_config_family); michael@0: } michael@0: if (outStyle) { michael@0: *outStyle = GetFontStyle(match); michael@0: } michael@0: return true; michael@0: michael@0: //////////////////// michael@0: michael@0: int count; michael@0: FcPattern** match = MatchFont(font_set, post_config_family, &count); michael@0: if (!match) { michael@0: FcPatternDestroy(pattern); michael@0: FcFontSetDestroy(font_set); michael@0: return NULL; michael@0: } michael@0: michael@0: FcPatternDestroy(pattern); michael@0: michael@0: SkTDArray trimmedMatches; michael@0: for (int i = 0; i < count; ++i) { michael@0: const char* justName = find_just_name(get_name(match[i], FC_FILE)); michael@0: if (!is_lower(*justName)) { michael@0: *trimmedMatches.append() = match[i]; michael@0: } michael@0: } michael@0: michael@0: SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC, michael@0: (trimmedMatches.begin(), michael@0: trimmedMatches.count())); michael@0: #endif michael@0: return false; michael@0: }