diff options
-rw-r--r-- | src/ports/SkFontHost_fontconfig.cpp | 345 | ||||
-rw-r--r-- | src/ports/SkFontHost_gamma_none.cpp | 34 |
2 files changed, 379 insertions, 0 deletions
diff --git a/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp new file mode 100644 index 0000000000..b13a84b9e4 --- /dev/null +++ b/src/ports/SkFontHost_fontconfig.cpp @@ -0,0 +1,345 @@ +/* libs/graphics/ports/SkFontHost_fontconfig.cpp +** +** Copyright 2008, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// ----------------------------------------------------------------------------- +// 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. +// ----------------------------------------------------------------------------- +static SkMutex 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; + +// This is the maximum size of the font cache. +static const unsigned kFontCacheMemoryBudget = 2 * 1024 * 1024; // 2MB + +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); +} + +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(bool is_fallback, + const char* type, FcType vtype, const void* value, + ...) +{ + va_list ap; + va_start(ap, value); + + FcPattern* pattern = FcPatternCreate(); + bool family_requested = false; + + for (;;) { + FcValue fcvalue; + fcvalue.type = vtype; + switch (vtype) { + case FcTypeString: + fcvalue.u.s = (FcChar8*) value; + break; + case FcTypeInteger: + fcvalue.u.i = (int) value; + break; + default: + SkASSERT(!"FontMatch unhandled type"); + } + FcPatternAdd(pattern, type, fcvalue, 0); + + if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0) + family_requested = true; + + 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 + 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 && !is_fallback) { + 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[], + 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(false, 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(false, + 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) +{ + SkASSERT(!"SkFontHost::CreateTypefaceFromStream unimplemented"); + return NULL; +} + +// static +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) +{ + SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); + return NULL; +} + +// static +bool SkFontHost::ValidFontID(SkFontID uniqueID) { + SkAutoMutexAcquire ac(global_fc_map_lock); + return global_fc_typefaces.find(uniqueID) != global_fc_typefaces.end(); +} + +// 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())); +} + +void SkFontHost::Serialize(const SkTypeface*, SkWStream*) { + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + SkASSERT(!"SkFontHost::Deserialize unimplemented"); + return NULL; +} + +// static +uint32_t SkFontHost::NextLogicalFont(SkFontID fontID) { + // We don't handle font fallback, WebKit does. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + if (sizeAllocatedSoFar > kFontCacheMemoryBudget) + return sizeAllocatedSoFar - kFontCacheMemoryBudget; + else + return 0; // nothing to do +} diff --git a/src/ports/SkFontHost_gamma_none.cpp b/src/ports/SkFontHost_gamma_none.cpp new file mode 100644 index 0000000000..37f190d421 --- /dev/null +++ b/src/ports/SkFontHost_gamma_none.cpp @@ -0,0 +1,34 @@ +/* Copyright 2008, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// ----------------------------------------------------------------------------- +// This is a noop gamma implementation for systems where gamma is already +// corrected, or dealt with in a system wide fashion. For example, on X windows +// one uses the xgamma utility to set the server-wide gamma correction value. +// ----------------------------------------------------------------------------- + +#include "SkFontHost.h" + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ + tables[0] = NULL; + tables[1] = NULL; +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + return 0; +} + |