Remove old and unused Android font host code
R=bungeman@google.com, scroggo@google.com Review URL: https://codereview.chromium.org/18666003 git-svn-id: http://skia.googlecode.com/svn/trunk@9940 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
13b3aa1466
commit
dc7919f228
@ -1,297 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 The Android Open Source Project
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "FontHostConfiguration_android.h"
|
||||
#include "SkString.h"
|
||||
#include "SkTDArray.h"
|
||||
#include <expat.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
|
||||
#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
|
||||
#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
|
||||
|
||||
|
||||
// These defines are used to determine the kind of tag that we're currently
|
||||
// populating with data. We only care about the sibling tags nameset and fileset
|
||||
// for now.
|
||||
#define NO_TAG 0
|
||||
#define NAMESET_TAG 1
|
||||
#define FILESET_TAG 2
|
||||
|
||||
/**
|
||||
* The FamilyData structure is passed around by the parser so that each handler
|
||||
* can read these variables that are relevant to the current parsing.
|
||||
*/
|
||||
struct FamilyData {
|
||||
FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef, const AndroidLocale &localeRef) :
|
||||
parser(parserRef), families(familiesRef), currentTag(NO_TAG),
|
||||
locale(localeRef), currentFamilyLangMatch(false), familyLangMatchCount(0) {}
|
||||
|
||||
XML_Parser *parser; // The expat parser doing the work
|
||||
SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed
|
||||
FontFamily *currentFamily; // The current family being created
|
||||
int currentTag; // A flag to indicate whether we're in nameset/fileset tags
|
||||
const AndroidLocale &locale; // The locale to which we compare the "lang" attribute of File.
|
||||
bool currentFamilyLangMatch; // If currentFamily's File has a "lang" attribute and matches locale.
|
||||
int familyLangMatchCount; // Number of families containing File which has a "lang" attribute and matches locale.
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for arbitrary text. This is used to parse the text inside each name
|
||||
* or file tag. The resulting strings are put into the fNames or fFileNames arrays.
|
||||
*/
|
||||
void textHandler(void *data, const char *s, int len) {
|
||||
FamilyData *familyData = (FamilyData*) data;
|
||||
// Make sure we're in the right state to store this name information
|
||||
if (familyData->currentFamily &&
|
||||
(familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
|
||||
// Malloc new buffer to store the string
|
||||
char *buff;
|
||||
buff = (char*) malloc((len + 1) * sizeof(char));
|
||||
strncpy(buff, s, len);
|
||||
buff[len] = '\0';
|
||||
switch (familyData->currentTag) {
|
||||
case NAMESET_TAG:
|
||||
*(familyData->currentFamily->fNames.append()) = buff;
|
||||
break;
|
||||
case FILESET_TAG:
|
||||
*(familyData->currentFamily->fFileNames.append()) = buff;
|
||||
break;
|
||||
default:
|
||||
// Noop - don't care about any text that's not in the Fonts or Names list
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the start of a tag. The only tags we expect are family, nameset,
|
||||
* fileset, name, and file.
|
||||
*/
|
||||
void startElementHandler(void *data, const char *tag, const char **atts) {
|
||||
FamilyData *familyData = (FamilyData*) data;
|
||||
int len = strlen(tag);
|
||||
if (strncmp(tag, "family", len)== 0) {
|
||||
familyData->currentFamily = new FontFamily();
|
||||
familyData->currentFamily->order = -1;
|
||||
// The Family tag has an optional "order" attribute with an integer value >= 0
|
||||
// If this attribute does not exist, the default value is -1
|
||||
for (int i = 0; atts[i] != NULL; i += 2) {
|
||||
const char* valueString = atts[i+1];
|
||||
int value;
|
||||
int len = sscanf(valueString, "%d", &value);
|
||||
if (len > 0) {
|
||||
familyData->currentFamily->order = value;
|
||||
}
|
||||
}
|
||||
} else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
|
||||
familyData->currentTag = NAMESET_TAG;
|
||||
} else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
|
||||
familyData->currentTag = FILESET_TAG;
|
||||
} else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
|
||||
XML_SetCharacterDataHandler(*familyData->parser, textHandler);
|
||||
} else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
|
||||
// From JB MR1, the File tag has a "lang" attribute to specify a language specific font file
|
||||
// and the family entry has higher priority than the others without "lang" attribute.
|
||||
bool includeTheEntry = true;
|
||||
for (int i = 0; atts[i] != NULL; i += 2) {
|
||||
const char* attribute = atts[i];
|
||||
const char* value = atts[i+1];
|
||||
if (strncmp(attribute, "lang", 4) == 0) {
|
||||
if (strcmp(value, familyData->locale.language) == 0) {
|
||||
// Found matching "lang" attribute. The current Family will have higher priority in the family list.
|
||||
familyData->currentFamilyLangMatch = true;
|
||||
} else {
|
||||
// Don't include the entry if "lang" is specified but not matching.
|
||||
includeTheEntry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (includeTheEntry) {
|
||||
XML_SetCharacterDataHandler(*familyData->parser, textHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the end of tags. We only care about family, nameset, fileset,
|
||||
* name, and file.
|
||||
*/
|
||||
void endElementHandler(void *data, const char *tag) {
|
||||
FamilyData *familyData = (FamilyData*) data;
|
||||
int len = strlen(tag);
|
||||
if (strncmp(tag, "family", len)== 0) {
|
||||
// Done parsing a Family - store the created currentFamily in the families array
|
||||
if (familyData->currentFamilyLangMatch) {
|
||||
*familyData->families.insert(familyData->familyLangMatchCount++) = familyData->currentFamily;
|
||||
familyData->currentFamilyLangMatch = false;
|
||||
} else {
|
||||
*familyData->families.append() = familyData->currentFamily;
|
||||
}
|
||||
familyData->currentFamily = NULL;
|
||||
} else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
|
||||
familyData->currentTag = NO_TAG;
|
||||
} else if (len == 7 && strncmp(tag, "fileset", len)== 0) {
|
||||
familyData->currentTag = NO_TAG;
|
||||
} else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
|
||||
(strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
|
||||
// Disable the arbitrary text handler installed to load Name data
|
||||
XML_SetCharacterDataHandler(*familyData->parser, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the persistent locale.
|
||||
*/
|
||||
void getLocale(AndroidLocale &locale)
|
||||
{
|
||||
char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX];
|
||||
__system_property_get("persist.sys.language", propLang);
|
||||
__system_property_get("persist.sys.country", propRegn);
|
||||
|
||||
if (*propLang == 0 && *propRegn == 0) {
|
||||
/* Set to ro properties, default is en_US */
|
||||
__system_property_get("ro.product.locale.language", propLang);
|
||||
__system_property_get("ro.product.locale.region", propRegn);
|
||||
if (*propLang == 0 && *propRegn == 0) {
|
||||
strcpy(propLang, "en");
|
||||
strcpy(propRegn, "US");
|
||||
}
|
||||
}
|
||||
strncpy(locale.language, propLang, 2);
|
||||
locale.language[2] = '\0';
|
||||
strncpy(locale.region, propRegn, 2);
|
||||
locale.region[2] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the current system locale (language and region) to open the best matching
|
||||
* customization. For example, when the language is Japanese, the sequence might be:
|
||||
* /system/etc/fallback_fonts-ja-JP.xml
|
||||
* /system/etc/fallback_fonts-ja.xml
|
||||
* /system/etc/fallback_fonts.xml
|
||||
*/
|
||||
FILE* openLocalizedFile(const char* origname, const AndroidLocale& locale) {
|
||||
FILE* file = 0;
|
||||
SkString basename;
|
||||
SkString filename;
|
||||
|
||||
basename.set(origname);
|
||||
// Remove the .xml suffix. We'll add it back in a moment.
|
||||
if (basename.endsWith(".xml")) {
|
||||
basename.resize(basename.size()-4);
|
||||
}
|
||||
// Try first with language and region
|
||||
filename.printf("%s-%s-%s.xml", basename.c_str(), locale.language, locale.region);
|
||||
file = fopen(filename.c_str(), "r");
|
||||
if (!file) {
|
||||
// If not found, try next with just language
|
||||
filename.printf("%s-%s.xml", basename.c_str(), locale.language);
|
||||
file = fopen(filename.c_str(), "r");
|
||||
|
||||
if (!file) {
|
||||
// If still not found, try just the original name
|
||||
file = fopen(origname, "r");
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function parses the given filename and stores the results in the given
|
||||
* families array.
|
||||
*/
|
||||
void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
|
||||
AndroidLocale locale;
|
||||
getLocale(locale);
|
||||
XML_Parser parser = XML_ParserCreate(NULL);
|
||||
FamilyData familyData(&parser, families, locale);
|
||||
XML_SetUserData(parser, &familyData);
|
||||
XML_SetElementHandler(parser, startElementHandler, endElementHandler);
|
||||
FILE *file = openLocalizedFile(filename, locale);
|
||||
// Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
|
||||
// are optional - failure here is okay because one of these optional files may not exist.
|
||||
if (file == NULL) {
|
||||
return;
|
||||
}
|
||||
char buffer[512];
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
fgets(buffer, sizeof(buffer), file);
|
||||
int len = strlen(buffer);
|
||||
if (feof(file) != 0) {
|
||||
done = true;
|
||||
}
|
||||
XML_Parse(parser, buffer, len, done);
|
||||
}
|
||||
fclose(file);
|
||||
XML_ParserFree(parser);
|
||||
}
|
||||
|
||||
void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
|
||||
parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
|
||||
}
|
||||
|
||||
void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
|
||||
SkTDArray<FontFamily*> vendorFonts;
|
||||
parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
|
||||
parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
|
||||
|
||||
// This loop inserts the vendor fallback fonts in the correct order in the
|
||||
// overall fallbacks list.
|
||||
int currentOrder = -1;
|
||||
for (int i = 0; i < vendorFonts.count(); ++i) {
|
||||
FontFamily* family = vendorFonts[i];
|
||||
int order = family->order;
|
||||
if (order < 0) {
|
||||
if (currentOrder < 0) {
|
||||
// Default case - just add it to the end of the fallback list
|
||||
*fallbackFonts.append() = family;
|
||||
} else {
|
||||
// no order specified on this font, but we're incrementing the order
|
||||
// based on an earlier order insertion request
|
||||
*fallbackFonts.insert(currentOrder++) = family;
|
||||
}
|
||||
} else {
|
||||
// Add the font into the fallback list in the specified order. Set
|
||||
// currentOrder for correct placement of other fonts in the vendor list.
|
||||
*fallbackFonts.insert(order) = family;
|
||||
currentOrder = order + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads data on font families from various expected configuration files. The
|
||||
* resulting data is returned in the given fontFamilies array.
|
||||
*/
|
||||
void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
|
||||
SkTDArray<FontFamily*> fallbackFonts;
|
||||
|
||||
getSystemFontFamilies(fontFamilies);
|
||||
getFallbackFontFamilies(fallbackFonts);
|
||||
|
||||
// Append all fallback fonts to system fonts
|
||||
for (int i = 0; i < fallbackFonts.count(); ++i) {
|
||||
*fontFamilies.append() = fallbackFonts[i];
|
||||
}
|
||||
}
|
||||
|
||||
void getTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
|
||||
const char* testMainConfigFile,
|
||||
const char* testFallbackConfigFile) {
|
||||
parseConfigFile(testMainConfigFile, fontFamilies);
|
||||
|
||||
SkTDArray<FontFamily*> fallbackFonts;
|
||||
parseConfigFile(testFallbackConfigFile, fallbackFonts);
|
||||
|
||||
// Append all fallback fonts to system fonts
|
||||
for (int i = 0; i < fallbackFonts.count(); ++i) {
|
||||
*fontFamilies.append() = fallbackFonts[i];
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 The Android Open Source Project
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef FONTHOSTCONFIGURATION_ANDROID_H_
|
||||
#define FONTHOSTCONFIGURATION_ANDROID_H_
|
||||
|
||||
#include "SkTDArray.h"
|
||||
|
||||
/**
|
||||
* The FontFamily data structure is created during parsing and handed back to
|
||||
* Skia to fold into its representation of font families. fNames is the list of
|
||||
* font names that alias to a font family. fFileNames is the list of font
|
||||
* filenames for the family. Order is the priority order for the font. This is
|
||||
* used internally to determine the order in which to place fallback fonts as
|
||||
* they are read from the configuration files.
|
||||
*/
|
||||
struct FontFamily {
|
||||
SkTDArray<const char*> fNames;
|
||||
SkTDArray<const char*> fFileNames;
|
||||
int order;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses all system font configuration files and returns the results in an
|
||||
* array of FontFamily structures.
|
||||
*/
|
||||
void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies);
|
||||
|
||||
/**
|
||||
* Parse the fallback and vendor system font configuration files and return the
|
||||
* results in an array of FontFamily structures.
|
||||
*/
|
||||
void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts);
|
||||
|
||||
/**
|
||||
* Parses all test font configuration files and returns the results in an
|
||||
* array of FontFamily structures.
|
||||
*/
|
||||
void getTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
|
||||
const char* testMainConfigFile,
|
||||
const char* testFallbackConfigFile);
|
||||
|
||||
struct AndroidLocale {
|
||||
char language[3];
|
||||
char region[3];
|
||||
};
|
||||
|
||||
void getLocale(AndroidLocale &locale);
|
||||
|
||||
#endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */
|
@ -1,944 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006 The Android Open Source Project
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkFontHost.h"
|
||||
#include "SkFontHost_FreeType_common.h"
|
||||
#include "SkFontDescriptor.h"
|
||||
#include "SkGraphics.h"
|
||||
#include "SkDescriptor.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkString.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkThread.h"
|
||||
#include "SkTSearch.h"
|
||||
#include "SkTypeface_android.h"
|
||||
#include "FontHostConfiguration_android.h"
|
||||
|
||||
#ifndef SK_FONT_FILE_PREFIX
|
||||
#define SK_FONT_FILE_PREFIX "/fonts/"
|
||||
#endif
|
||||
|
||||
#ifndef SK_DEBUG_FONTS
|
||||
#define SK_DEBUG_FONTS 0
|
||||
#endif
|
||||
|
||||
// For test only.
|
||||
static const char* gTestMainConfigFile = NULL;
|
||||
static const char* gTestFallbackConfigFile = NULL;
|
||||
static const char* gTestFontFilePrefix = NULL;
|
||||
|
||||
bool find_name_and_attributes(SkStream* stream, SkString* name,
|
||||
SkTypeface::Style* style, bool* isFixedPitch);
|
||||
|
||||
static void GetFullPathForSysFonts(SkString* full, const char name[]) {
|
||||
if (gTestFontFilePrefix) {
|
||||
full->set(gTestFontFilePrefix);
|
||||
} else {
|
||||
full->set(getenv("ANDROID_ROOT"));
|
||||
full->append(SK_FONT_FILE_PREFIX);
|
||||
}
|
||||
full->append(name);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct FamilyRec;
|
||||
|
||||
/* This guy holds a mapping of a name -> family, used for looking up fonts.
|
||||
Since it is stored in a stretchy array that doesn't preserve object
|
||||
semantics, we don't use constructor/destructors, but just have explicit
|
||||
helpers to manage our internal bookkeeping.
|
||||
*/
|
||||
struct NameFamilyPair {
|
||||
const char* fName; // we own this
|
||||
FamilyRec* fFamily; // we don't own this, we just reference it
|
||||
|
||||
void construct(const char name[], FamilyRec* family) {
|
||||
fName = strdup(name);
|
||||
fFamily = family; // we don't own this, so just record the referene
|
||||
}
|
||||
|
||||
void destruct() {
|
||||
free((char*)fName);
|
||||
// we don't own family, so just ignore our reference
|
||||
}
|
||||
};
|
||||
typedef SkTDArray<NameFamilyPair> NameFamilyPairList;
|
||||
|
||||
// we use atomic_inc to grow this for each typeface we create
|
||||
static int32_t gUniqueFontID;
|
||||
|
||||
// this is the mutex that protects gFamilyHead and GetNameList()
|
||||
SK_DECLARE_STATIC_MUTEX(gFamilyHeadAndNameListMutex);
|
||||
static FamilyRec* gFamilyHead;
|
||||
|
||||
static NameFamilyPairList& GetNameList() {
|
||||
/*
|
||||
* It is assumed that the caller has already acquired a lock on
|
||||
* gFamilyHeadAndNameListMutex before calling this.
|
||||
*/
|
||||
static NameFamilyPairList* gNameList;
|
||||
if (NULL == gNameList) {
|
||||
gNameList = SkNEW(NameFamilyPairList);
|
||||
// register a delete proc with sk_atexit(..) when available
|
||||
}
|
||||
return *gNameList;
|
||||
}
|
||||
|
||||
struct FamilyRec {
|
||||
FamilyRec* fNext;
|
||||
SkTypeface* fFaces[4];
|
||||
|
||||
FamilyRec()
|
||||
{
|
||||
fNext = gFamilyHead;
|
||||
memset(fFaces, 0, sizeof(fFaces));
|
||||
gFamilyHead = this;
|
||||
}
|
||||
};
|
||||
|
||||
static SkTypeface* find_best_face(const FamilyRec* family,
|
||||
SkTypeface::Style style) {
|
||||
SkTypeface* const* faces = family->fFaces;
|
||||
|
||||
if (faces[style] != NULL) { // exact match
|
||||
return faces[style];
|
||||
}
|
||||
// look for a matching bold
|
||||
style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
|
||||
if (faces[style] != NULL) {
|
||||
return faces[style];
|
||||
}
|
||||
// look for the plain
|
||||
if (faces[SkTypeface::kNormal] != NULL) {
|
||||
return faces[SkTypeface::kNormal];
|
||||
}
|
||||
// look for anything
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (faces[i] != NULL) {
|
||||
return faces[i];
|
||||
}
|
||||
}
|
||||
// should never get here, since the faces list should not be empty
|
||||
SkDEBUGFAIL("faces list is empty");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static FamilyRec* find_family(const SkTypeface* member) {
|
||||
FamilyRec* curr = gFamilyHead;
|
||||
while (curr != NULL) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (curr->fFaces[i] == member) {
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
curr = curr->fNext;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// gFamilyHeadAndNameListMutex must already be acquired
|
||||
static const char* find_family_name(const SkTypeface* member) {
|
||||
FamilyRec* family = find_family(member);
|
||||
if (NULL == family) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NameFamilyPairList& namelist = GetNameList();
|
||||
NameFamilyPair* list = namelist.begin();
|
||||
int count = namelist.count();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
NameFamilyPair* pair = &list[i];
|
||||
if (pair->fFamily == family) {
|
||||
return pair->fName;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns the matching typeface, or NULL. If a typeface is found, its refcnt
|
||||
is not modified.
|
||||
*/
|
||||
static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
|
||||
FamilyRec* curr = gFamilyHead;
|
||||
while (curr != NULL) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SkTypeface* face = curr->fFaces[i];
|
||||
if (face != NULL && face->uniqueID() == uniqueID) {
|
||||
return face;
|
||||
}
|
||||
}
|
||||
curr = curr->fNext;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove reference to this face from its family. If the resulting family
|
||||
is empty (has no faces), return that family, otherwise return NULL
|
||||
*/
|
||||
static FamilyRec* remove_from_family(const SkTypeface* face) {
|
||||
FamilyRec* family = find_family(face);
|
||||
if (family) {
|
||||
SkASSERT(family->fFaces[face->style()] == face);
|
||||
family->fFaces[face->style()] = NULL;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (family->fFaces[i] != NULL) { // family is non-empty
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SkDebugf("remove_from_family(%p) face not found", face);
|
||||
}
|
||||
return family; // return the empty family
|
||||
}
|
||||
|
||||
// maybe we should make FamilyRec be doubly-linked
|
||||
static void detach_and_delete_family(FamilyRec* family) {
|
||||
FamilyRec* curr = gFamilyHead;
|
||||
FamilyRec* prev = NULL;
|
||||
|
||||
while (curr != NULL) {
|
||||
FamilyRec* next = curr->fNext;
|
||||
if (curr == family) {
|
||||
if (prev == NULL) {
|
||||
gFamilyHead = next;
|
||||
} else {
|
||||
prev->fNext = next;
|
||||
}
|
||||
SkDELETE(family);
|
||||
return;
|
||||
}
|
||||
prev = curr;
|
||||
curr = next;
|
||||
}
|
||||
SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
|
||||
}
|
||||
|
||||
// gFamilyHeadAndNameListMutex must already be acquired
|
||||
static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
|
||||
NameFamilyPairList& namelist = GetNameList();
|
||||
NameFamilyPair* list = namelist.begin();
|
||||
int count = namelist.count();
|
||||
|
||||
int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
|
||||
|
||||
if (index >= 0) {
|
||||
return find_best_face(list[index].fFamily, style);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// gFamilyHeadAndNameListMutex must already be acquired
|
||||
static SkTypeface* find_typeface(const SkTypeface* familyMember,
|
||||
SkTypeface::Style style) {
|
||||
const FamilyRec* family = find_family(familyMember);
|
||||
return family ? find_best_face(family, style) : NULL;
|
||||
}
|
||||
|
||||
// gFamilyHeadAndNameListMutex must already be acquired
|
||||
static void add_name(const char name[], FamilyRec* family) {
|
||||
SkAutoAsciiToLC tolc(name);
|
||||
name = tolc.lc();
|
||||
|
||||
NameFamilyPairList& namelist = GetNameList();
|
||||
NameFamilyPair* list = namelist.begin();
|
||||
int count = namelist.count();
|
||||
|
||||
int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
|
||||
|
||||
if (index < 0) {
|
||||
list = namelist.insert(~index);
|
||||
list->construct(name, family);
|
||||
}
|
||||
}
|
||||
|
||||
// gFamilyHeadAndNameListMutex must already be acquired
|
||||
static void remove_from_names(FamilyRec* emptyFamily) {
|
||||
#ifdef SK_DEBUG
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SkASSERT(emptyFamily->fFaces[i] == NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
SkTDArray<NameFamilyPair>& list = GetNameList();
|
||||
|
||||
// must go backwards when removing
|
||||
for (int i = list.count() - 1; i >= 0; --i) {
|
||||
NameFamilyPair* pair = &list[i];
|
||||
if (pair->fFamily == emptyFamily) {
|
||||
pair->destruct();
|
||||
list.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FamilyTypeface : public SkTypeface_FreeType {
|
||||
public:
|
||||
FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember,
|
||||
bool isFixedPitch)
|
||||
: INHERITED(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedPitch) {
|
||||
fIsSysFont = sysFont;
|
||||
|
||||
// our caller has acquired the gFamilyHeadAndNameListMutex so this is safe
|
||||
FamilyRec* rec = NULL;
|
||||
if (familyMember) {
|
||||
rec = find_family(familyMember);
|
||||
SkASSERT(rec);
|
||||
} else {
|
||||
rec = SkNEW(FamilyRec);
|
||||
}
|
||||
rec->fFaces[style] = this;
|
||||
}
|
||||
|
||||
virtual ~FamilyTypeface() {
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
|
||||
// remove us from our family. If the family is now empty, we return
|
||||
// that and then remove that family from the name list
|
||||
FamilyRec* family = remove_from_family(this);
|
||||
if (NULL != family) {
|
||||
remove_from_names(family);
|
||||
detach_and_delete_family(family);
|
||||
}
|
||||
}
|
||||
|
||||
bool isSysFont() const { return fIsSysFont; }
|
||||
|
||||
virtual const char* getUniqueString() const = 0;
|
||||
virtual const char* getFilePath() const = 0;
|
||||
|
||||
protected:
|
||||
virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
|
||||
|
||||
private:
|
||||
bool fIsSysFont;
|
||||
|
||||
typedef SkTypeface_FreeType INHERITED;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class StreamTypeface : public FamilyTypeface {
|
||||
public:
|
||||
StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
|
||||
SkStream* stream, bool isFixedPitch)
|
||||
: INHERITED(style, sysFont, familyMember, isFixedPitch) {
|
||||
SkASSERT(stream);
|
||||
stream->ref();
|
||||
fStream = stream;
|
||||
}
|
||||
virtual ~StreamTypeface() {
|
||||
fStream->unref();
|
||||
}
|
||||
|
||||
virtual const char* getUniqueString() const SK_OVERRIDE { return NULL; }
|
||||
virtual const char* getFilePath() const SK_OVERRIDE { return NULL; }
|
||||
|
||||
protected:
|
||||
virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
|
||||
*ttcIndex = 0;
|
||||
// we just ref our existing stream, since the caller will call unref()
|
||||
// when they are through
|
||||
fStream->ref();
|
||||
// must rewind each time, since the caller assumes a "new" stream
|
||||
fStream->rewind();
|
||||
return fStream;
|
||||
}
|
||||
|
||||
private:
|
||||
SkStream* fStream;
|
||||
|
||||
typedef FamilyTypeface INHERITED;
|
||||
};
|
||||
|
||||
class FileTypeface : public FamilyTypeface {
|
||||
public:
|
||||
FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
|
||||
const char path[], bool isFixedPitch)
|
||||
: INHERITED(style, sysFont, familyMember, isFixedPitch) {
|
||||
SkString fullpath;
|
||||
|
||||
if (sysFont) {
|
||||
GetFullPathForSysFonts(&fullpath, path);
|
||||
path = fullpath.c_str();
|
||||
}
|
||||
fPath.set(path);
|
||||
}
|
||||
|
||||
virtual const char* getUniqueString() const SK_OVERRIDE {
|
||||
const char* str = strrchr(fPath.c_str(), '/');
|
||||
if (str) {
|
||||
str += 1; // skip the '/'
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
virtual const char* getFilePath() const SK_OVERRIDE {
|
||||
return fPath.c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
|
||||
*ttcIndex = 0;
|
||||
return SkStream::NewFromFile(fPath.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
SkString fPath;
|
||||
|
||||
typedef FamilyTypeface INHERITED;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool get_name_and_style(const char path[], SkString* name,
|
||||
SkTypeface::Style* style,
|
||||
bool* isFixedPitch, bool isExpected) {
|
||||
SkString fullpath;
|
||||
GetFullPathForSysFonts(&fullpath, path);
|
||||
|
||||
SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(fullpath.c_str()));
|
||||
if (stream.get()) {
|
||||
return find_name_and_attributes(stream, name, style, isFixedPitch);
|
||||
} else {
|
||||
if (isExpected) {
|
||||
SkDebugf("---- failed to open <%s> as a font", fullpath.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// used to record our notion of the pre-existing fonts
|
||||
struct FontInitRec {
|
||||
const char* fFileName;
|
||||
const char* const* fNames; // null-terminated list
|
||||
};
|
||||
|
||||
// deliberately empty, but we use the address to identify fallback fonts
|
||||
static const char* gFBNames[] = { NULL };
|
||||
|
||||
/* Fonts are grouped by family, with the first font in a family having the
|
||||
list of names (even if that list is empty), and the following members having
|
||||
null for the list. The names list must be NULL-terminated.
|
||||
*/
|
||||
static FontInitRec *gSystemFonts;
|
||||
static size_t gNumSystemFonts = 0;
|
||||
|
||||
// these globals are assigned (once) by load_system_fonts()
|
||||
static FamilyRec* gDefaultFamily;
|
||||
static SkTypeface* gDefaultNormal;
|
||||
static char** gDefaultNames = NULL;
|
||||
static uint32_t *gFallbackFonts;
|
||||
|
||||
#if SK_DEBUG_FONTS
|
||||
static void dump_globals() {
|
||||
SkDebugf("gDefaultNormal=%p id=%u refCnt=%d", gDefaultNormal,
|
||||
gDefaultNormal ? gDefaultNormal->uniqueID() : 0,
|
||||
gDefaultNormal ? gDefaultNormal->getRefCnt() : 0);
|
||||
|
||||
if (gDefaultFamily) {
|
||||
SkDebugf("gDefaultFamily=%p fFaces={%u,%u,%u,%u} refCnt={%d,%d,%d,%d}",
|
||||
gDefaultFamily,
|
||||
gDefaultFamily->fFaces[0] ? gDefaultFamily->fFaces[0]->uniqueID() : 0,
|
||||
gDefaultFamily->fFaces[1] ? gDefaultFamily->fFaces[1]->uniqueID() : 0,
|
||||
gDefaultFamily->fFaces[2] ? gDefaultFamily->fFaces[2]->uniqueID() : 0,
|
||||
gDefaultFamily->fFaces[3] ? gDefaultFamily->fFaces[3]->uniqueID() : 0,
|
||||
gDefaultFamily->fFaces[0] ? gDefaultFamily->fFaces[0]->getRefCnt() : 0,
|
||||
gDefaultFamily->fFaces[1] ? gDefaultFamily->fFaces[1]->getRefCnt() : 0,
|
||||
gDefaultFamily->fFaces[2] ? gDefaultFamily->fFaces[2]->getRefCnt() : 0,
|
||||
gDefaultFamily->fFaces[3] ? gDefaultFamily->fFaces[3]->getRefCnt() : 0);
|
||||
} else {
|
||||
SkDebugf("gDefaultFamily=%p", gDefaultFamily);
|
||||
}
|
||||
|
||||
SkDebugf("gNumSystemFonts=%d gSystemFonts=%p gFallbackFonts=%p",
|
||||
gNumSystemFonts, gSystemFonts, gFallbackFonts);
|
||||
|
||||
for (size_t i = 0; i < gNumSystemFonts; ++i) {
|
||||
SkDebugf("gSystemFonts[%d] fileName=%s", i, gSystemFonts[i].fFileName);
|
||||
size_t namesIndex = 0;
|
||||
if (gSystemFonts[i].fNames)
|
||||
for (const char* fontName = gSystemFonts[i].fNames[namesIndex];
|
||||
fontName != 0;
|
||||
fontName = gSystemFonts[i].fNames[++namesIndex]) {
|
||||
SkDebugf(" name[%u]=%s", namesIndex, fontName);
|
||||
}
|
||||
}
|
||||
|
||||
if (gFamilyHead) {
|
||||
FamilyRec* rec = gFamilyHead;
|
||||
int i=0;
|
||||
while (rec) {
|
||||
SkDebugf("gFamilyHead[%d]=%p fFaces={%u,%u,%u,%u} refCnt={%d,%d,%d,%d}",
|
||||
i++, rec,
|
||||
rec->fFaces[0] ? rec->fFaces[0]->uniqueID() : 0,
|
||||
rec->fFaces[1] ? rec->fFaces[1]->uniqueID() : 0,
|
||||
rec->fFaces[2] ? rec->fFaces[2]->uniqueID() : 0,
|
||||
rec->fFaces[3] ? rec->fFaces[3]->uniqueID() : 0,
|
||||
rec->fFaces[0] ? rec->fFaces[0]->getRefCnt() : 0,
|
||||
rec->fFaces[1] ? rec->fFaces[1]->getRefCnt() : 0,
|
||||
rec->fFaces[2] ? rec->fFaces[2]->getRefCnt() : 0,
|
||||
rec->fFaces[3] ? rec->fFaces[3]->getRefCnt() : 0);
|
||||
rec = rec->fNext;
|
||||
}
|
||||
} else {
|
||||
SkDebugf("gFamilyHead=%p", gFamilyHead);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Load info from a configuration file that populates the system/fallback font structures
|
||||
*/
|
||||
static void load_font_info() {
|
||||
SkTDArray<FontFamily*> fontFamilies;
|
||||
if (gTestMainConfigFile) {
|
||||
getTestFontFamilies(fontFamilies, gTestMainConfigFile, gTestFallbackConfigFile);
|
||||
} else {
|
||||
getFontFamilies(fontFamilies);
|
||||
}
|
||||
|
||||
SkTDArray<FontInitRec> fontInfo;
|
||||
for (int i = 0; i < fontFamilies.count(); ++i) {
|
||||
FontFamily *family = fontFamilies[i];
|
||||
for (int j = 0; j < family->fFileNames.count(); ++j) {
|
||||
FontInitRec fontInfoRecord;
|
||||
fontInfoRecord.fFileName = family->fFileNames[j];
|
||||
if (j == 0) {
|
||||
if (family->fNames.count() == 0) {
|
||||
// Fallback font
|
||||
fontInfoRecord.fNames = (char **)gFBNames;
|
||||
} else {
|
||||
SkTDArray<const char*> names = family->fNames;
|
||||
const char **nameList = (const char**)
|
||||
malloc((names.count() + 1) * sizeof(char*));
|
||||
if (nameList == NULL) {
|
||||
// shouldn't get here
|
||||
SkDEBUGFAIL("Failed to allocate nameList");
|
||||
break;
|
||||
}
|
||||
if (gDefaultNames == NULL) {
|
||||
gDefaultNames = (char**) nameList;
|
||||
}
|
||||
for (int i = 0; i < names.count(); ++i) {
|
||||
nameList[i] = names[i];
|
||||
}
|
||||
nameList[names.count()] = NULL;
|
||||
fontInfoRecord.fNames = nameList;
|
||||
}
|
||||
} else {
|
||||
fontInfoRecord.fNames = NULL;
|
||||
}
|
||||
*fontInfo.append() = fontInfoRecord;
|
||||
}
|
||||
}
|
||||
gNumSystemFonts = fontInfo.count();
|
||||
gSystemFonts = (FontInitRec*) malloc(gNumSystemFonts * sizeof(FontInitRec));
|
||||
gFallbackFonts = (uint32_t*) malloc((gNumSystemFonts + 1) * sizeof(uint32_t));
|
||||
if (gSystemFonts == NULL) {
|
||||
// shouldn't get here
|
||||
SkDEBUGFAIL("No system fonts were found");
|
||||
gNumSystemFonts = 0;
|
||||
}
|
||||
|
||||
#if SK_DEBUG_FONTS
|
||||
SkDebugf("---- We have %d system fonts", gNumSystemFonts);
|
||||
#endif
|
||||
for (size_t i = 0; i < gNumSystemFonts; ++i) {
|
||||
gSystemFonts[i].fFileName = fontInfo[i].fFileName;
|
||||
gSystemFonts[i].fNames = fontInfo[i].fNames;
|
||||
#if SK_DEBUG_FONTS
|
||||
SkDebugf("---- gSystemFonts[%d] fileName=%s", i, fontInfo[i].fFileName);
|
||||
#endif
|
||||
}
|
||||
fontFamilies.deleteAll();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called once (ensured by the sentinel check at the beginning of our body).
|
||||
* Initializes all the globals, and register the system fonts.
|
||||
*
|
||||
* gFamilyHeadAndNameListMutex must already be acquired.
|
||||
*/
|
||||
static void init_system_fonts() {
|
||||
// check if we've already been called
|
||||
if (gDefaultNormal) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkASSERT(gUniqueFontID == 0);
|
||||
|
||||
load_font_info();
|
||||
|
||||
FontInitRec* rec = gSystemFonts;
|
||||
SkTypeface* firstInFamily = NULL;
|
||||
int fallbackCount = 0;
|
||||
|
||||
for (size_t i = 0; i < gNumSystemFonts; i++) {
|
||||
// if we're the first in a new family, clear firstInFamily
|
||||
if (rec[i].fNames != NULL) {
|
||||
firstInFamily = NULL;
|
||||
}
|
||||
|
||||
bool isFixedPitch;
|
||||
SkString name;
|
||||
SkTypeface::Style style;
|
||||
|
||||
// we expect all the fonts, except the "fallback" fonts
|
||||
bool isExpected = (rec[i].fNames != gFBNames);
|
||||
if (!get_name_and_style(rec[i].fFileName, &name, &style,
|
||||
&isFixedPitch, isExpected)) {
|
||||
// We need to increase gUniqueFontID here so that the unique id of
|
||||
// each font matches its index in gSystemFonts array, as expected
|
||||
// by find_uniqueID.
|
||||
sk_atomic_inc(&gUniqueFontID);
|
||||
continue;
|
||||
}
|
||||
|
||||
SkTypeface* tf = SkNEW_ARGS(FileTypeface,
|
||||
(style,
|
||||
true, // system-font (cannot delete)
|
||||
firstInFamily, // what family to join
|
||||
rec[i].fFileName,
|
||||
isFixedPitch) // filename
|
||||
);
|
||||
#if SK_DEBUG_FONTS
|
||||
SkDebugf("---- SkTypeface[%d] %s fontID %d",
|
||||
i, rec[i].fFileName, tf->uniqueID());
|
||||
#endif
|
||||
|
||||
if (rec[i].fNames != NULL) {
|
||||
// see if this is one of our fallback fonts
|
||||
if (rec[i].fNames == gFBNames) {
|
||||
#if SK_DEBUG_FONTS
|
||||
SkDebugf("---- adding %s as fallback[%d] fontID %d",
|
||||
rec[i].fFileName, fallbackCount, tf->uniqueID());
|
||||
#endif
|
||||
gFallbackFonts[fallbackCount++] = tf->uniqueID();
|
||||
|
||||
// Use the font file name as the name of the typeface.
|
||||
const char **nameList = (const char**)malloc(2 * sizeof(char*));
|
||||
if (nameList == NULL) {
|
||||
// shouldn't get here
|
||||
SkDEBUGFAIL("Failed to allocate nameList");
|
||||
break;
|
||||
}
|
||||
nameList[0] = rec[i].fFileName;
|
||||
nameList[1] = NULL;
|
||||
rec[i].fNames = nameList;
|
||||
}
|
||||
|
||||
firstInFamily = tf;
|
||||
FamilyRec* family = find_family(tf);
|
||||
const char* const* names = rec[i].fNames;
|
||||
|
||||
// record the default family if this is it
|
||||
if (names == gDefaultNames) {
|
||||
gDefaultFamily = family;
|
||||
}
|
||||
// add the names to map to this family
|
||||
while (*names) {
|
||||
add_name(*names, family);
|
||||
names += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do this after all fonts are loaded. This is our default font, and it
|
||||
// acts as a sentinel so we only execute load_system_fonts() once
|
||||
gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
|
||||
// now terminate our fallback list with the sentinel value
|
||||
gFallbackFonts[fallbackCount] = 0;
|
||||
|
||||
#if SK_DEBUG_FONTS
|
||||
SkDEBUGCODE(dump_globals());
|
||||
#endif
|
||||
}
|
||||
|
||||
static size_t find_uniqueID(const char* filename) {
|
||||
// uniqueID is the index, offset by one, of the associated element in
|
||||
// gSystemFonts[] (assumes system fonts are loaded before external fonts)
|
||||
// return 0 if not found
|
||||
const FontInitRec* rec = gSystemFonts;
|
||||
for (size_t i = 0; i < gNumSystemFonts; i++) {
|
||||
if (strcmp(rec[i].fFileName, filename) == 0) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reload_fallback_fonts() {
|
||||
if (gTestFallbackConfigFile) {
|
||||
// No need to reload fallback fonts in test environment.
|
||||
return;
|
||||
}
|
||||
|
||||
SkGraphics::PurgeFontCache();
|
||||
|
||||
SkTDArray<FontFamily*> fallbackFamilies;
|
||||
getFallbackFontFamilies(fallbackFamilies);
|
||||
|
||||
int fallbackCount = 0;
|
||||
for (int i = 0; i < fallbackFamilies.count(); ++i) {
|
||||
FontFamily *family = fallbackFamilies[i];
|
||||
|
||||
for (int j = 0; j < family->fFileNames.count(); ++j) {
|
||||
if (family->fFileNames[j]) {
|
||||
|
||||
// ensure the fallback font exists before adding it to the list
|
||||
bool isFixedPitch;
|
||||
SkString name;
|
||||
SkTypeface::Style style;
|
||||
if (!get_name_and_style(family->fFileNames[j], &name, &style,
|
||||
&isFixedPitch, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t uniqueID = find_uniqueID(family->fFileNames[j]);
|
||||
SkASSERT(uniqueID != 0);
|
||||
#if SK_DEBUG_FONTS
|
||||
SkDebugf("---- reload %s as fallback[%d] fontID %d oldFontID %d",
|
||||
family->fFileNames[j], fallbackCount, uniqueID,
|
||||
gFallbackFonts[fallbackCount]);
|
||||
#endif
|
||||
gFallbackFonts[fallbackCount++] = uniqueID;
|
||||
break; // The fallback set contains only the first font of each family
|
||||
}
|
||||
}
|
||||
}
|
||||
// reset the sentinel the end of the newly ordered array
|
||||
gFallbackFonts[fallbackCount] = 0;
|
||||
}
|
||||
|
||||
static void load_system_fonts() {
|
||||
static AndroidLocale prevLocale;
|
||||
AndroidLocale locale;
|
||||
|
||||
getLocale(locale);
|
||||
|
||||
if (!gDefaultNormal) {
|
||||
prevLocale = locale;
|
||||
init_system_fonts();
|
||||
} else if (strncmp(locale.language, prevLocale.language, 2) ||
|
||||
strncmp(locale.region, prevLocale.region, 2)) {
|
||||
prevLocale = locale;
|
||||
reload_fallback_fonts();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FamilyTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
|
||||
bool* isLocalStream) const {
|
||||
{
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
desc->setFamilyName(find_family_name(this));
|
||||
desc->setFontFileName(this->getUniqueString());
|
||||
}
|
||||
*isLocalStream = !this->isSysFont();
|
||||
}
|
||||
|
||||
#if 0 // do we need this different name lookup for Deserialize?
|
||||
SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
|
||||
...
|
||||
if (NULL != fontFileName && 0 != *fontFileName) {
|
||||
const FontInitRec* rec = gSystemFonts;
|
||||
for (size_t i = 0; i < gNumSystemFonts; i++) {
|
||||
if (strcmp(rec[i].fFileName, fontFileName) == 0) {
|
||||
// backup until we hit the fNames
|
||||
for (int j = i; j >= 0; --j) {
|
||||
if (rec[j].fNames != NULL) {
|
||||
return SkFontHost::CreateTypeface(NULL,
|
||||
rec[j].fNames[0], style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
|
||||
const char familyName[],
|
||||
SkTypeface::Style style) {
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
|
||||
load_system_fonts();
|
||||
|
||||
// clip to legal style bits
|
||||
style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
|
||||
|
||||
SkTypeface* tf = NULL;
|
||||
|
||||
if (NULL != familyFace) {
|
||||
tf = find_typeface(familyFace, style);
|
||||
} else if (NULL != familyName) {
|
||||
// SkDebugf("======= familyName <%s>\n", familyName);
|
||||
tf = find_typeface(familyName, style);
|
||||
}
|
||||
|
||||
if (NULL == tf) {
|
||||
tf = find_best_face(gDefaultFamily, style);
|
||||
}
|
||||
|
||||
// we ref(), since the semantic is to return a new instance
|
||||
tf->ref();
|
||||
return tf;
|
||||
}
|
||||
|
||||
SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
|
||||
const SkPaintOptionsAndroid& options) {
|
||||
// Skia does not support font fallback by default for ndk applications. This
|
||||
// enables clients such as WebKit to customize their font selection. In any
|
||||
// case clients can use GetFallbackFamilyNameForChar() to get the fallback
|
||||
// font for individual characters.
|
||||
if (!options.isUsingFontFallbacks()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
|
||||
load_system_fonts();
|
||||
|
||||
const SkTypeface* origTypeface = find_from_uniqueID(origFontID);
|
||||
const SkTypeface* currTypeface = find_from_uniqueID(currFontID);
|
||||
|
||||
SkASSERT(origTypeface != 0);
|
||||
SkASSERT(currTypeface != 0);
|
||||
SkASSERT(gFallbackFonts);
|
||||
|
||||
// Our fallback list always stores the id of the plain in each fallback
|
||||
// family, so we transform currFontID to its plain equivalent.
|
||||
currFontID = find_typeface(currTypeface, SkTypeface::kNormal)->uniqueID();
|
||||
|
||||
/* First see if fontID is already one of our fallbacks. If so, return
|
||||
its successor. If fontID is not in our list, then return the first one
|
||||
in our list. Note: list is zero-terminated, and returning zero means
|
||||
we have no more fonts to use for fallbacks.
|
||||
*/
|
||||
const uint32_t* list = gFallbackFonts;
|
||||
for (int i = 0; list[i] != 0; i++) {
|
||||
if (list[i] == currFontID) {
|
||||
if (list[i+1] == 0)
|
||||
return NULL;
|
||||
const SkTypeface* nextTypeface = find_from_uniqueID(list[i+1]);
|
||||
return SkRef(find_typeface(nextTypeface, origTypeface->style()));
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, currFontID was not a fallback, so we start at the
|
||||
// beginning of our list. Assuming there is at least one fallback font,
|
||||
// i.e. gFallbackFonts[0] != 0.
|
||||
const SkTypeface* firstTypeface = find_from_uniqueID(list[0]);
|
||||
return SkRef(find_typeface(firstTypeface, origTypeface->style()));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
|
||||
if (NULL == stream || stream->getLength() <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool isFixedPitch;
|
||||
SkTypeface::Style style;
|
||||
|
||||
if (find_name_and_attributes(stream, NULL, &style, &isFixedPitch)) {
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
// Make sure system fonts are loaded to comply with the assumption of
|
||||
// unique id offset by one in find_uniqueID.
|
||||
load_system_fonts();
|
||||
return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedPitch));
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
|
||||
SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
|
||||
return stream.get() ? SkFontHost::CreateTypefaceFromStream(stream) : NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Function from SkTypeface_android.h
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// this function can't be called if the gFamilyHeadAndNameListMutex is already locked
|
||||
static bool typefaceContainsChar(SkTypeface* face, SkUnichar uni) {
|
||||
SkPaint paint;
|
||||
paint.setTypeface(face);
|
||||
paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
|
||||
|
||||
uint16_t glyphID;
|
||||
paint.textToGlyphs(&uni, sizeof(uni), &glyphID);
|
||||
return glyphID != 0;
|
||||
}
|
||||
|
||||
// this function can't be called if the gFamilyHeadAndNameListMutex is already locked
|
||||
static SkTypeface* findFallbackTypefaceForChar(SkUnichar uni) {
|
||||
SkASSERT(gFallbackFonts);
|
||||
const uint32_t* list = gFallbackFonts;
|
||||
for (int i = 0; list[i] != 0; i++) {
|
||||
SkTypeface* face;
|
||||
{
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
face = find_from_uniqueID(list[i]);
|
||||
}
|
||||
if (typefaceContainsChar(face, uni)) {
|
||||
return face;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) {
|
||||
SkASSERT(name);
|
||||
{
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
load_system_fonts();
|
||||
}
|
||||
|
||||
const SkTypeface* tf = findFallbackTypefaceForChar(uni);
|
||||
if (!tf) {
|
||||
return false;
|
||||
}
|
||||
SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
|
||||
name->set(find_family_name(tf));
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackconf,
|
||||
const char* fontsdir) {
|
||||
gTestMainConfigFile = mainconf;
|
||||
gTestFallbackConfigFile = fallbackconf;
|
||||
gTestFontFilePrefix = fontsdir;
|
||||
SkASSERT(gTestMainConfigFile);
|
||||
SkASSERT(gTestFallbackConfigFile);
|
||||
SkASSERT(gTestFontFilePrefix);
|
||||
SkDEBUGF(("Use Test Config File Main %s, Fallback %s, Font Dir %s",
|
||||
gTestMainConfigFile, gTestFallbackConfigFile, gTestFontFilePrefix));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SkFontMgr.h"
|
||||
|
||||
SkFontMgr* SkFontMgr::Factory() {
|
||||
// todo
|
||||
return NULL;
|
||||
}
|
Loading…
Reference in New Issue
Block a user