blob: 3cbccaf36e0b10d3277bc962b8941ecb243f68cb [file] [log] [blame]
/* 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 <stdio.h>
/* 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 <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
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)
}