/* libs/graphics/ports/SkFontHost_android.cpp ** ** Copyright 2006, The Android Open Source Project ** ** 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. */ #include "SkFontHost.h" #include "SkDescriptor.h" #include "SkString.h" #include "SkStream.h" #include /* define this if we can use mmap() to access fonts from the filesystem */ #define SK_CAN_USE_MMAP #ifndef SK_FONTPATH #define SK_FONTPATH "the complete path for a font file" #endif struct FontFaceRec { const char* fFileName; uint8_t fFamilyIndex; SkBool8 fBold; SkBool8 fItalic; static const FontFaceRec& FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic); }; struct FontFamilyRec { const FontFaceRec* fFaces; int fFaceCount; }; const FontFaceRec& FontFaceRec::FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic) { SkASSERT(count > 0); int i; // look for an exact match for (i = 0; i < count; i++) { if (rec[i].fBold == isBold && rec[i].fItalic == isItalic) return rec[i]; } // look for a match in the bold field for (i = 0; i < count; i++) { if (rec[i].fBold == isBold) return rec[i]; } // look for a normal/regular face for (i = 0; i < count; i++) { if (!rec[i].fBold && !rec[i].fItalic) return rec[i]; } // give up return rec[0]; } enum { DEFAULT_FAMILY_INDEX, FAMILY_INDEX_COUNT }; static const FontFaceRec gDefaultFaces[] = { { SK_FONTPATH, DEFAULT_FAMILY_INDEX, 0, 0 } }; // This table must be in the same order as the ..._FAMILY_INDEX enum specifies static const FontFamilyRec gFamilies[] = { { gDefaultFaces, SK_ARRAY_COUNT(gDefaultFaces) } }; #define DEFAULT_FAMILY_INDEX DEFAULT_FAMILY_INDEX #define DEFAULT_FAMILY_FACE_INDEX 0 //////////////////////////////////////////////////////////////////////////////////////// /* map common "web" font names to our font list */ struct FontFamilyMatchRec { const char* fLCName; int fFamilyIndex; }; /* This is a table of synonyms for collapsing font names down to their pseudo-equivalents (i.e. in terms of fonts we actually have.) Keep this sorted by the first field so we can do a binary search. If this gets big, we could switch to a hash... */ static const FontFamilyMatchRec gMatches[] = { #if 0 { "Ahem", Ahem_FAMILY_INDEX }, { "arial", SANS_FAMILY_INDEX }, { "courier", MONO_FAMILY_INDEX }, { "courier new", MONO_FAMILY_INDEX }, { "cursive", SERIF_FAMILY_INDEX }, { "fantasy", SERIF_FAMILY_INDEX }, { "georgia", SERIF_FAMILY_INDEX }, { "goudy", SERIF_FAMILY_INDEX }, { "helvetica", SANS_FAMILY_INDEX }, { "palatino", SERIF_FAMILY_INDEX }, { "tahoma", SANS_FAMILY_INDEX }, { "sans-serif", SANS_FAMILY_INDEX }, { "serif", SERIF_FAMILY_INDEX }, { "times", SERIF_FAMILY_INDEX }, { "times new roman", SERIF_FAMILY_INDEX }, { "verdana", SANS_FAMILY_INDEX } #endif }; //////////////////////////////////////////////////////////////////////////////////////// #include "SkTSearch.h" static bool contains_only_ascii(const char s[]) { for (;;) { int c = *s++; if (c == 0) break; if ((c >> 7) != 0) return false; } return true; } #define TRACE_FONT_NAME(code) //#define TRACE_FONT_NAME(code) code const FontFamilyRec* find_family_rec(const char target[]) { int index; // If we're asked for a font name that contains non-ascii, // 1) SkStrLCSearch can't handle it // 2) All of our fonts are have ascii names, so... TRACE_FONT_NAME(printf("----------------- font request <%s>", target);) if (contains_only_ascii(target)) { // Search for the font by matching the entire name index = SkStrLCSearch(&gMatches[0].fLCName, SK_ARRAY_COUNT(gMatches), target, sizeof(gMatches[0])); if (index >= 0) { TRACE_FONT_NAME(printf(" found %d\n", index);) return &gFamilies[gMatches[index].fFamilyIndex]; } } // Sniff for key words... #if 0 if (strstr(target, "sans") || strstr(target, "Sans")) { TRACE_FONT_NAME(printf(" found sans\n");) return &gFamilies[SANS_FAMILY_INDEX]; } if (strstr(target, "serif") || strstr(target, "Serif")) { TRACE_FONT_NAME(printf(" found serif\n");) return &gFamilies[SERIF_FAMILY_INDEX]; } if (strstr(target, "mono") || strstr(target, "Mono")) { TRACE_FONT_NAME(printf(" found mono\n");) return &gFamilies[MONO_FAMILY_INDEX]; } #endif TRACE_FONT_NAME(printf(" use default\n");) // we give up, just give them the default font return &gFamilies[DEFAULT_FAMILY_INDEX]; } /////////////////////////////////////////////////////////////////////////////////////////////// static const FontFaceRec* get_default_face() { return &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX]; } class FontFaceRec_Typeface : public SkTypeface { public: FontFaceRec_Typeface(const FontFaceRec& face) : fFace(face) { int style = 0; if (face.fBold) style |= SkTypeface::kBold; if (face.fItalic) style |= SkTypeface::kItalic; this->setStyle((SkTypeface::Style)style); } // This global const reference completely identifies the face const FontFaceRec& fFace; }; static const FontFaceRec* get_typeface_rec(const SkTypeface* face) { const FontFaceRec_Typeface* f = (FontFaceRec_Typeface*)face; return f ? &f->fFace : get_default_face(); } static uint32_t ptr2uint32(const void* p) { // cast so we avoid warnings on 64bit machines that a ptr difference // which might be 64bits is being trucated from 64 to 32 return (uint32_t)((char*)p - (char*)0); } uint32_t SkFontHost::TypefaceHash(const SkTypeface* face) { // just use our address as the hash value return ptr2uint32(get_typeface_rec(face)); } bool SkFontHost::TypefaceEqual(const SkTypeface* facea, const SkTypeface* faceb) { return get_typeface_rec(facea) == get_typeface_rec(faceb); } SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) { const FontFamilyRec* family; if (familyFace) family = &gFamilies[((FontFaceRec_Typeface*)familyFace)->fFace.fFamilyIndex]; else if (familyName) family = find_family_rec(familyName); else family = &gFamilies[DEFAULT_FAMILY_INDEX]; const FontFaceRec& face = FontFaceRec::FindFace(family->fFaces, family->fFaceCount, (style & SkTypeface::kBold) != 0, (style & SkTypeface::kItalic) != 0); // if we're returning our input parameter, no need to create a new instance if (familyFace != NULL && &((FontFaceRec_Typeface*)familyFace)->fFace == &face) { familyFace->ref(); return (SkTypeface*)familyFace; } return SkNEW_ARGS(FontFaceRec_Typeface, (face)); } uint32_t SkFontHost::FlattenTypeface(const SkTypeface* tface, void* buffer) { const FontFaceRec* face; if (tface) face = &((const FontFaceRec_Typeface*)tface)->fFace; else face = get_default_face(); size_t size = sizeof(face); if (buffer) memcpy(buffer, &face, size); return size; } void SkFontHost::GetDescriptorKeyString(const SkDescriptor* desc, SkString* key) { key->set(SK_FONTPATH); } #ifdef SK_CAN_USE_MMAP #include #include #include #include class SkMMAPStream : public SkMemoryStream { public: SkMMAPStream(const char filename[]); virtual ~SkMMAPStream(); virtual void setMemory(const void* data, size_t length); private: int fFildes; void* fAddr; size_t fSize; void closeMMap(); typedef SkMemoryStream INHERITED; }; SkMMAPStream::SkMMAPStream(const char filename[]) { fFildes = -1; // initialize to failure case int fildes = open(filename, O_RDONLY); if (fildes < 0) { SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno)); return; } off_t size = lseek(fildes, 0, SEEK_END); // find the file size if (size == -1) { SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno)); close(fildes); return; } (void)lseek(fildes, 0, SEEK_SET); // restore file offset to beginning void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0); if (MAP_FAILED == addr) { SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno)); close(fildes); return; } this->INHERITED::setMemory(addr, size); fFildes = fildes; fAddr = addr; fSize = size; } SkMMAPStream::~SkMMAPStream() { this->closeMMap(); } void SkMMAPStream::setMemory(const void* data, size_t length) { this->closeMMap(); this->INHERITED::setMemory(data, length); } void SkMMAPStream::closeMMap() { if (fFildes >= 0) { munmap(fAddr, fSize); close(fFildes); fFildes = -1; } } #endif SkStream* SkFontHost::OpenDescriptorStream(const SkDescriptor* desc, const char keyString[]) { // our key string IS our filename, so we can ignore desc SkStream* strm; #ifdef SK_CAN_USE_MMAP strm = new SkMMAPStream(keyString); if (strm->getLength() > 0) return strm; // strm not valid delete strm; // fall through to FILEStream attempt #endif strm = new SkFILEStream(keyString); if (strm->getLength() > 0) return strm; // strm not valid delete strm; return NULL; } SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) { const FontFaceRec* face = get_default_face(); SkAutoDescriptor ad(sizeof(rec) + sizeof(face) + SkDescriptor::ComputeOverhead(2)); SkDescriptor* desc = ad.getDesc(); desc->init(); desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); desc->addEntry(kTypeface_SkDescriptorTag, sizeof(face), &face); desc->computeChecksum(); return SkFontHost::CreateScalerContext(desc); } size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { return 0; // nothing to do (change me if you want to limit the font cache) } int SkFontHost::ComputeGammaFlag(const SkPaint& paint) { return 0; } void SkFontHost::GetGammaTables(const uint8_t* tables[2]) { tables[0] = NULL; // black gamma (e.g. exp=1.4) tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) }