/external/skia/src/ports/SkFontHost_fontconfig.cpp
C++ | 354 lines | 214 code | 53 blank | 87 comment | 36 complexity | dd44996917787d7770eb158d7bbe6916 MD5 | raw file
- /*
- * Copyright 2008 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- // -----------------------------------------------------------------------------
- // This file provides implementations of the font resolution members of
- // SkFontHost by using the fontconfig[1] library. Fontconfig is usually found
- // on Linux systems and handles configuration, parsing and caching issues
- // involved with enumerating and matching fonts.
- //
- // [1] http://fontconfig.org
- // -----------------------------------------------------------------------------
- #include <map>
- #include <string>
- #include <fontconfig/fontconfig.h>
- #include "SkFontHost.h"
- #include "SkStream.h"
- // This is an extern from SkFontHost_FreeType
- SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
- // -----------------------------------------------------------------------------
- // The rest of Skia requires that fonts be identified by a unique unsigned id
- // and that we be able to load them given the id. What we actually get from
- // fontconfig is the filename of the font so we keep a locked map from
- // filenames to fileid numbers and back.
- //
- // Note that there's also a unique id in the SkTypeface. This is unique over
- // both filename and style. Thus we encode that id as (fileid << 8) | style.
- // Although truetype fonts can support multiple faces in a single file, at the
- // moment Skia doesn't.
- // -----------------------------------------------------------------------------
- SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
- static std::map<std::string, unsigned> global_fc_map;
- static std::map<unsigned, std::string> global_fc_map_inverted;
- static std::map<uint32_t, SkTypeface *> global_fc_typefaces;
- static unsigned global_fc_map_next_id = 0;
- static unsigned UniqueIdToFileId(unsigned uniqueid)
- {
- return uniqueid >> 8;
- }
- static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid)
- {
- return static_cast<SkTypeface::Style>(uniqueid & 0xff);
- }
- static unsigned FileIdAndStyleToUniqueId(unsigned fileid,
- SkTypeface::Style style)
- {
- SkASSERT((style & 0xff) == style);
- return (fileid << 8) | static_cast<int>(style);
- }
- // -----------------------------------------------------------------------------
- // Normally we only return exactly the font asked for. In last-resort cases,
- // the request is for one of the basic font names "Sans", "Serif" or
- // "Monospace". This function tells you whether a given request is for such a
- // fallback.
- // -----------------------------------------------------------------------------
- static bool IsFallbackFontAllowed(const char* request)
- {
- return strcmp(request, "Sans") == 0 ||
- strcmp(request, "Serif") == 0 ||
- strcmp(request, "Monospace") == 0;
- }
- class FontConfigTypeface : public SkTypeface {
- public:
- FontConfigTypeface(Style style, uint32_t id)
- : SkTypeface(style, id)
- { }
- };
- // -----------------------------------------------------------------------------
- // Find a matching font where @type (one of FC_*) is equal to @value. For a
- // list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
- // The variable arguments are a list of triples, just like the first three
- // arguments, and must be NULL terminated.
- //
- // For example,
- // FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf",
- // NULL);
- // -----------------------------------------------------------------------------
- static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
- ...)
- {
- va_list ap;
- va_start(ap, value);
- FcPattern* pattern = FcPatternCreate();
- const char* family_requested = NULL;
- for (;;) {
- FcValue fcvalue;
- fcvalue.type = vtype;
- switch (vtype) {
- case FcTypeString:
- fcvalue.u.s = (FcChar8*) value;
- break;
- case FcTypeInteger:
- fcvalue.u.i = (int)(intptr_t)value;
- break;
- default:
- SkDEBUGFAIL("FontMatch unhandled type");
- }
- FcPatternAdd(pattern, type, fcvalue, 0);
- if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0)
- family_requested = (const char*) value;
- type = va_arg(ap, const char *);
- if (!type)
- break;
- // FcType is promoted to int when passed through ...
- vtype = static_cast<FcType>(va_arg(ap, int));
- value = va_arg(ap, const void *);
- };
- va_end(ap);
- FcConfigSubstitute(0, pattern, FcMatchPattern);
- FcDefaultSubstitute(pattern);
- // Font matching:
- // CSS often specifies a fallback list of families:
- // font-family: a, b, c, serif;
- // However, fontconfig will always do its best to find *a* font when asked
- // for something so we need a way to tell if the match which it has found is
- // "good enough" for us. Otherwise, we can return NULL which gets piped up
- // and lets WebKit know to try the next CSS family name. However, fontconfig
- // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
- // wish to support that.
- //
- // Thus, if a specific family is requested we set @family_requested. Then we
- // record two strings: the family name after config processing and the
- // family name after resolving. If the two are equal, it's a good match.
- //
- // So consider the case where a user has mapped Arial to Helvetica in their
- // config.
- // requested family: "Arial"
- // post_config_family: "Helvetica"
- // post_match_family: "Helvetica"
- // -> good match
- //
- // and for a missing font:
- // requested family: "Monaco"
- // post_config_family: "Monaco"
- // post_match_family: "Times New Roman"
- // -> BAD match
- //
- // However, we special-case fallback fonts; see IsFallbackFontAllowed().
- FcChar8* post_config_family;
- FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);
- FcResult result;
- FcPattern* match = FcFontMatch(0, pattern, &result);
- if (!match) {
- FcPatternDestroy(pattern);
- return NULL;
- }
- FcChar8* post_match_family;
- FcPatternGetString(match, FC_FAMILY, 0, &post_match_family);
- const bool family_names_match =
- !family_requested ?
- true :
- strcasecmp((char *)post_config_family, (char *)post_match_family) == 0;
- FcPatternDestroy(pattern);
- if (!family_names_match && !IsFallbackFontAllowed(family_requested)) {
- FcPatternDestroy(match);
- return NULL;
- }
- return match;
- }
- // -----------------------------------------------------------------------------
- // Check to see if the filename has already been assigned a fileid and, if so,
- // use it. Otherwise, assign one. Return the resulting fileid.
- // -----------------------------------------------------------------------------
- static unsigned FileIdFromFilename(const char* filename)
- {
- SkAutoMutexAcquire ac(global_fc_map_lock);
- std::map<std::string, unsigned>::const_iterator i =
- global_fc_map.find(filename);
- if (i == global_fc_map.end()) {
- const unsigned fileid = global_fc_map_next_id++;
- global_fc_map[filename] = fileid;
- global_fc_map_inverted[fileid] = filename;
- return fileid;
- } else {
- return i->second;
- }
- }
- // static
- SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
- const char familyName[],
- const void* data, size_t bytelength,
- SkTypeface::Style style)
- {
- const char* resolved_family_name = NULL;
- FcPattern* face_match = NULL;
- {
- SkAutoMutexAcquire ac(global_fc_map_lock);
- FcInit();
- }
- if (familyFace) {
- // Here we use the inverted global id map to find the filename from the
- // SkTypeface object. Given the filename we can ask fontconfig for the
- // familyname of the font.
- SkAutoMutexAcquire ac(global_fc_map_lock);
- const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID());
- std::map<unsigned, std::string>::const_iterator i =
- global_fc_map_inverted.find(fileid);
- if (i == global_fc_map_inverted.end())
- return NULL;
- FcInit();
- face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(),
- NULL);
- if (!face_match)
- return NULL;
- FcChar8* family;
- if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) {
- FcPatternDestroy(face_match);
- return NULL;
- }
- // At this point, @family is pointing into the @face_match object so we
- // cannot release it yet.
- resolved_family_name = reinterpret_cast<char*>(family);
- } else if (familyName) {
- resolved_family_name = familyName;
- } else {
- return NULL;
- }
- // At this point, we have a resolved_family_name from somewhere
- SkASSERT(resolved_family_name);
- const int bold = style & SkTypeface::kBold ?
- FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
- const int italic = style & SkTypeface::kItalic ?
- FC_SLANT_ITALIC : FC_SLANT_ROMAN;
- FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
- FC_WEIGHT, FcTypeInteger, bold,
- FC_SLANT, FcTypeInteger, italic,
- NULL);
- if (face_match)
- FcPatternDestroy(face_match);
- if (!match)
- return NULL;
- FcChar8* filename;
- if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) {
- FcPatternDestroy(match);
- return NULL;
- }
- // Now @filename is pointing into @match
- const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
- const unsigned id = FileIdAndStyleToUniqueId(fileid, style);
- SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
- FcPatternDestroy(match);
- {
- SkAutoMutexAcquire ac(global_fc_map_lock);
- global_fc_typefaces[id] = typeface;
- }
- return typeface;
- }
- // static
- SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
- {
- SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
- return NULL;
- }
- // static
- SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
- {
- SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
- return NULL;
- }
- // static
- SkStream* SkFontHost::OpenStream(uint32_t id)
- {
- SkAutoMutexAcquire ac(global_fc_map_lock);
- const unsigned fileid = UniqueIdToFileId(id);
- std::map<unsigned, std::string>::const_iterator i =
- global_fc_map_inverted.find(fileid);
- if (i == global_fc_map_inverted.end())
- return NULL;
- return SkNEW_ARGS(SkFILEStream, (i->second.c_str()));
- }
- size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
- int32_t* index) {
- SkAutoMutexAcquire ac(global_fc_map_lock);
- const unsigned fileid = UniqueIdToFileId(fontID);
- std::map<unsigned, std::string>::const_iterator i =
- global_fc_map_inverted.find(fileid);
- if (i == global_fc_map_inverted.end()) {
- return 0;
- }
- const std::string& str = i->second;
- if (path) {
- memcpy(path, str.c_str(), SkMin32(str.size(), length));
- }
- if (index) { // TODO: check if we're in a TTC
- *index = 0;
- }
- return str.size();
- }
- void SkFontHost::Serialize(const SkTypeface*, SkWStream*) {
- SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
- }
- SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
- SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
- return NULL;
- }
- SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
- // We don't handle font fallback, WebKit does.
- return 0;
- }