| /* 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 <sys/mman.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "SkFontHost.h" |
| #include "SkStream.h" |
| #include "SkFontHost_fontconfig_control.h" |
| #include "SkFontHost_fontconfig_impl.h" |
| #include "SkFontHost_fontconfig_direct.h" |
| |
| static FontConfigInterface* global_fc_impl = NULL; |
| |
| void SkiaFontConfigUseDirectImplementation() { |
| if (global_fc_impl) |
| delete global_fc_impl; |
| global_fc_impl = new FontConfigDirect; |
| } |
| |
| void SkiaFontConfigSetImplementation(FontConfigInterface* font_config) { |
| if (global_fc_impl) |
| delete global_fc_impl; |
| global_fc_impl = font_config; |
| } |
| |
| static FontConfigInterface* GetFcImpl() { |
| if (!global_fc_impl) |
| global_fc_impl = new FontConfigDirect; |
| return global_fc_impl; |
| } |
| |
| static SkMutex global_fc_map_lock; |
| static std::map<uint32_t, SkTypeface *> global_fc_typefaces; |
| |
| static SkMutex global_remote_font_map_lock; |
| static std::map<uint32_t, std::pair<uint8_t*, size_t> > global_remote_fonts; |
| static unsigned global_next_remote_font_id; |
| |
| // This is the maximum size of the font cache. |
| static const unsigned kFontCacheMemoryBudget = 2 * 1024 * 1024; // 2MB |
| |
| // UniqueIds are encoded as (filefaceid << 8) | style |
| // For system fonts, filefaceid = (fileid << 4) | face_index. |
| // For remote fonts, filefaceid = fileid. |
| |
| static unsigned UniqueIdToFileFaceId(unsigned uniqueid) |
| { |
| return uniqueid >> 8; |
| } |
| |
| static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid) |
| { |
| return static_cast<SkTypeface::Style>(uniqueid & 0xff); |
| } |
| |
| static unsigned FileFaceIdAndStyleToUniqueId(unsigned filefaceid, |
| SkTypeface::Style style) |
| { |
| SkASSERT((style & 0xff) == style); |
| return (filefaceid << 8) | static_cast<int>(style); |
| } |
| |
| static const unsigned kRemoteFontMask = 0x00800000u; |
| |
| static bool IsRemoteFont(unsigned filefaceid) |
| { |
| return filefaceid & kRemoteFontMask; |
| } |
| |
| class FontConfigTypeface : public SkTypeface { |
| public: |
| FontConfigTypeface(Style style, uint32_t id) |
| : SkTypeface(style, id) |
| { } |
| |
| ~FontConfigTypeface() |
| { |
| const uint32_t id = uniqueID(); |
| if (IsRemoteFont(UniqueIdToFileFaceId(id))) { |
| SkAutoMutexAcquire ac(global_remote_font_map_lock); |
| std::map<uint32_t, std::pair<uint8_t*, size_t> >::iterator iter |
| = global_remote_fonts.find(id); |
| if (iter != global_remote_fonts.end()) { |
| sk_free(iter->second.first); // remove the font on memory. |
| global_remote_fonts.erase(iter); |
| } |
| } |
| } |
| }; |
| |
| // static |
| SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, |
| const char familyName[], |
| const void* data, size_t bytelength, |
| SkTypeface::Style style) |
| { |
| std::string resolved_family_name; |
| |
| if (familyFace) { |
| // Given the fileid we can ask fontconfig for the familyname of the |
| // font. |
| const unsigned filefaceid = UniqueIdToFileFaceId(familyFace->uniqueID()); |
| if (!GetFcImpl()->Match(&resolved_family_name, NULL, |
| true /* filefaceid valid */, filefaceid, "", |
| NULL, 0, NULL, NULL)) { |
| return NULL; |
| } |
| } else if (familyName) { |
| resolved_family_name = familyName; |
| } |
| |
| bool bold = style & SkTypeface::kBold; |
| bool italic = style & SkTypeface::kItalic; |
| unsigned filefaceid; |
| if (!GetFcImpl()->Match(NULL, &filefaceid, |
| false, -1, /* no filefaceid */ |
| resolved_family_name, data, bytelength, |
| &bold, &italic)) { |
| return NULL; |
| } |
| const SkTypeface::Style resulting_style = static_cast<SkTypeface::Style>( |
| (bold ? SkTypeface::kBold : 0) | |
| (italic ? SkTypeface::kItalic : 0)); |
| |
| const unsigned id = FileFaceIdAndStyleToUniqueId(filefaceid, |
| resulting_style); |
| SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (resulting_style, id)); |
| |
| { |
| SkAutoMutexAcquire ac(global_fc_map_lock); |
| global_fc_typefaces[id] = typeface; |
| } |
| |
| return typeface; |
| } |
| |
| // static |
| SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) |
| { |
| if (!stream) |
| return NULL; |
| |
| const size_t length = stream->read(0, 0); |
| if (!length) |
| return NULL; |
| if (length >= 1024 * 1024 * 1024) |
| return NULL; // don't accept too large fonts (>= 1GB) for safety. |
| |
| uint8_t* font = (uint8_t*)sk_malloc_throw(length); |
| if (stream->read(font, length) != length) { |
| sk_free(font); |
| return NULL; |
| } |
| |
| SkTypeface::Style style = static_cast<SkTypeface::Style>(0); |
| unsigned id = 0; |
| { |
| SkAutoMutexAcquire ac(global_remote_font_map_lock); |
| id = FileFaceIdAndStyleToUniqueId( |
| global_next_remote_font_id | kRemoteFontMask, style); |
| |
| if (++global_next_remote_font_id >= kRemoteFontMask) |
| global_next_remote_font_id = 0; |
| |
| if (!global_remote_fonts.insert( |
| std::make_pair(id, std::make_pair(font, length))).second) { |
| sk_free(font); |
| return NULL; |
| } |
| } |
| |
| SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id)); |
| return typeface; |
| } |
| |
| // static |
| SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) |
| { |
| SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); |
| return NULL; |
| } |
| |
| // static |
| bool SkFontHost::ValidFontID(SkFontID uniqueID) { |
| if (IsRemoteFont(UniqueIdToFileFaceId(uniqueID))) { |
| // remote font |
| SkAutoMutexAcquire ac(global_remote_font_map_lock); |
| return global_remote_fonts.find(uniqueID) != global_remote_fonts.end(); |
| } else { |
| // local font |
| SkAutoMutexAcquire ac(global_fc_map_lock); |
| return global_fc_typefaces.find(uniqueID) != global_fc_typefaces.end(); |
| } |
| } |
| |
| 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 curr, SkFontID orig) { |
| // We don't handle font fallback, WebKit does. |
| return 0; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| class SkFileDescriptorStream : public SkStream { |
| public: |
| SkFileDescriptorStream(int fd) { |
| memory_ = NULL; |
| offset_ = 0; |
| |
| struct stat st; |
| if (fstat(fd, &st)) |
| return; |
| |
| void* memory = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); |
| close(fd); |
| if (memory == MAP_FAILED) |
| return; |
| |
| memory_ = reinterpret_cast<uint8_t*>(memory); |
| length_ = st.st_size; |
| } |
| |
| ~SkFileDescriptorStream() { |
| munmap(const_cast<uint8_t*>(memory_), length_); |
| } |
| |
| virtual bool rewind() { |
| offset_ = 0; |
| return true; |
| } |
| |
| // SkStream implementation. |
| virtual size_t read(void* buffer, size_t size) { |
| if (!buffer && !size) { |
| // This is request for the length of the stream. |
| return length_; |
| } |
| |
| if (!buffer) { |
| // This is a request to skip bytes. |
| if (offset_ + size < offset_) |
| return offset_; |
| offset_ += size; |
| if (offset_ > length_) |
| offset_ = length_; |
| return offset_; |
| } |
| |
| size_t remaining = length_ - offset_; |
| if (size > remaining) |
| size = remaining; |
| memcpy(buffer, memory_ + offset_, size); |
| offset_ += size; |
| return size; |
| } |
| |
| virtual const void* getMemoryBase() { |
| return memory_; |
| } |
| |
| private: |
| const uint8_t* memory_; |
| size_t offset_, length_; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| // static |
| SkStream* SkFontHost::OpenStream(uint32_t id) |
| { |
| const unsigned filefaceid = UniqueIdToFileFaceId(id); |
| |
| if (IsRemoteFont(filefaceid)) { |
| // remote font |
| SkAutoMutexAcquire ac(global_remote_font_map_lock); |
| std::map<uint32_t, std::pair<uint8_t*, size_t> >::const_iterator iter |
| = global_remote_fonts.find(id); |
| if (iter == global_remote_fonts.end()) |
| return NULL; |
| return SkNEW_ARGS( |
| SkMemoryStream, (iter->second.first, iter->second.second)); |
| } |
| |
| // system font |
| const int fd = GetFcImpl()->Open(filefaceid); |
| if (fd < 0) |
| return NULL; |
| |
| return SkNEW_ARGS(SkFileDescriptorStream, (fd)); |
| } |
| |
| size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) |
| { |
| if (sizeAllocatedSoFar > kFontCacheMemoryBudget) |
| return sizeAllocatedSoFar - kFontCacheMemoryBudget; |
| else |
| return 0; // nothing to do |
| } |
| |
| // static |
| size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, |
| int32_t* index) { |
| const unsigned filefaceid = UniqueIdToFileFaceId(fontID); |
| |
| if (IsRemoteFont(filefaceid)) |
| return 0; |
| |
| if (index) { |
| *index = filefaceid & 0xfu; |
| // 1 is a bogus return value. |
| // We had better change the signature of this function in Skia |
| // to return bool to indicate success/failure and have another |
| // out param for fileName length. |
| if (!path) |
| return 1; |
| } |
| |
| if (path) |
| SkASSERT(!"SkFontHost::GetFileName does not support the font path " |
| "retrieval."); |
| |
| return 0; |
| } |