Merge "New weight-aware font config" into lmp-dev
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 1d465b3..bfb30b7 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -62,10 +62,26 @@
ALOGE("addFont failed to create font %s", str.c_str());
return false;
}
- FontFamily* fontFamily = (FontFamily*)familyPtr;
+ FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
return addSkTypeface(fontFamily, face);
}
+static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
+ jstring path, jint weight, jboolean isItalic) {
+ NPE_CHECK_RETURN_ZERO(env, path);
+ ScopedUtfChars str(env, path);
+ SkTypeface* face = SkTypeface::CreateFromFile(str.c_str());
+ if (face == NULL) {
+ ALOGE("addFont failed to create font %s", str.c_str());
+ return false;
+ }
+ FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
+ MinikinFont* minikinFont = new MinikinFontSkia(face);
+ fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic));
+ minikinFont->Unref();
+ return true;
+}
+
static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPtr,
jobject jassetMgr, jstring jpath) {
NPE_CHECK_RETURN_ZERO(env, jassetMgr);
@@ -92,17 +108,18 @@
ALOGE("addFontFromAsset failed to create font %s", str.c_str());
return false;
}
- FontFamily* fontFamily = (FontFamily*)familyPtr;
+ FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
return addSkTypeface(fontFamily, face);
}
///////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gFontFamilyMethods[] = {
- { "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create },
- { "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
- { "nAddFont", "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
- { "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
+ { "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create },
+ { "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
+ { "nAddFont", "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
+ { "nAddFontWeightStyle", "(JLjava/lang/String;IZ)Z", (void*)FontFamily_addFontWeightStyle },
+ { "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
(void*)FontFamily_addFontFromAsset },
};
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index cf4e838..2029658 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -41,6 +41,12 @@
return reinterpret_cast<jlong>(face);
}
+static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) {
+ TypefaceImpl* family = reinterpret_cast<TypefaceImpl*>(familyHandle);
+ TypefaceImpl* face = TypefaceImpl_createWeightAlias(family, weight);
+ return reinterpret_cast<jlong>(face);
+}
+
static void Typeface_unref(JNIEnv* env, jobject obj, jlong faceHandle) {
TypefaceImpl* face = reinterpret_cast<TypefaceImpl*>(faceHandle);
TypefaceImpl_unref(face);
@@ -65,6 +71,7 @@
static JNINativeMethod gTypefaceMethods[] = {
{ "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
+ { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias },
{ "nativeUnref", "(J)V", (void*)Typeface_unref },
{ "nativeGetStyle", "(J)I", (void*)Typeface_getStyle },
{ "nativeCreateFromArray", "([J)J",
diff --git a/core/jni/android/graphics/TypefaceImpl.cpp b/core/jni/android/graphics/TypefaceImpl.cpp
index 9ce6de1..7afbeb2 100644
--- a/core/jni/android/graphics/TypefaceImpl.cpp
+++ b/core/jni/android/graphics/TypefaceImpl.cpp
@@ -39,14 +39,17 @@
namespace android {
-// Any weight greater than or equal to this is considered "bold" for
-// legacy API.
-static const int kBoldThreshold = 6;
-
-static FontStyle styleFromSkiaStyle(SkTypeface::Style skiaStyle) {
- int weight = (skiaStyle & SkTypeface::kBold) != 0 ? 7 : 4;
- bool italic = (skiaStyle & SkTypeface::kItalic) != 0;
- return FontStyle(weight, italic);
+// Resolve the 1..9 weight based on base weight and bold flag
+static void resolveStyle(TypefaceImpl* typeface) {
+ int weight = typeface->fBaseWeight / 100;
+ if (typeface->fSkiaStyle & SkTypeface::kBold) {
+ weight += 3;
+ }
+ if (weight > 9) {
+ weight = 9;
+ }
+ bool italic = (typeface->fSkiaStyle & SkTypeface::kItalic) != 0;
+ typeface->fStyle = FontStyle(weight, italic);
}
TypefaceImpl* gDefaultTypeface = NULL;
@@ -90,7 +93,9 @@
// default so we can make progress before that happens.
gDefaultTypeface = new TypefaceImpl;
gDefaultTypeface->fFontCollection = makeFontCollection();
- gDefaultTypeface->fStyle = FontStyle();
+ gDefaultTypeface->fSkiaStyle = SkTypeface::kNormal;
+ gDefaultTypeface->fBaseWeight = 400;
+ resolveStyle(gDefaultTypeface);
}
}
@@ -109,25 +114,23 @@
if (result != 0) {
result->fFontCollection = resolvedFace->fFontCollection;
result->fFontCollection->Ref();
- result->fStyle = styleFromSkiaStyle(style);
+ result->fSkiaStyle = style;
+ result->fBaseWeight = resolvedFace->fBaseWeight;
+ resolveStyle(result);
}
return result;
}
-static TypefaceImpl* createFromSkTypeface(SkTypeface* typeface) {
- if (typeface == NULL) {
- return NULL;
- }
- MinikinFont* minikinFont = new MinikinFontSkia(typeface);
- std::vector<FontFamily *> typefaces;
- FontFamily* family = new FontFamily();
- family->addFont(minikinFont);
- minikinFont->Unref();
- typefaces.push_back(family);
+TypefaceImpl* TypefaceImpl_createWeightAlias(TypefaceImpl* src, int weight) {
+ TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(src);
TypefaceImpl* result = new TypefaceImpl;
- result->fFontCollection = new FontCollection(typefaces);
- family->Unref();
- result->fStyle = FontStyle(); // TODO: improve
+ if (result != 0) {
+ result->fFontCollection = resolvedFace->fFontCollection;
+ result->fFontCollection->Ref();
+ result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ result->fBaseWeight = weight;
+ resolveStyle(result);
+ }
return result;
}
@@ -141,7 +144,7 @@
result->fFontCollection = new FontCollection(familyVec);
if (size == 0) {
ALOGW("createFromFamilies creating empty collection");
- result->fStyle = FontStyle();
+ result->fSkiaStyle = SkTypeface::kNormal;
} else {
const FontStyle defaultStyle;
FontFamily* firstFamily = reinterpret_cast<FontFamily*>(families[0]);
@@ -150,11 +153,13 @@
SkTypeface* skTypeface = reinterpret_cast<MinikinFontSkia*>(mf)->GetSkTypeface();
// TODO: probably better to query more precise style from family, will be important
// when we open up API to access 100..900 weights
- result->fStyle = styleFromSkiaStyle(skTypeface->style());
+ result->fSkiaStyle = skTypeface->style();
} else {
- result->fStyle = defaultStyle;
+ result->fSkiaStyle = SkTypeface::kNormal;
}
}
+ result->fBaseWeight = 400;
+ resolveStyle(result);
return result;
}
@@ -166,12 +171,7 @@
}
int TypefaceImpl_getStyle(TypefaceImpl* face) {
- FontStyle style = face->fStyle;
- int result = style.getItalic() ? SkTypeface::kItalic : 0;
- if (style.getWeight() >= kBoldThreshold) {
- result |= SkTypeface::kBold;
- }
- return result;
+ return face->fSkiaStyle;
}
void TypefaceImpl_setDefault(TypefaceImpl* face) {
diff --git a/core/jni/android/graphics/TypefaceImpl.h b/core/jni/android/graphics/TypefaceImpl.h
index 12b3403..d129f621 100644
--- a/core/jni/android/graphics/TypefaceImpl.h
+++ b/core/jni/android/graphics/TypefaceImpl.h
@@ -28,6 +28,13 @@
struct TypefaceImpl {
FontCollection *fFontCollection;
+
+ // style used for constructing and querying Typeface objects
+ SkTypeface::Style fSkiaStyle;
+ // base weight in CSS-style units, 100..900
+ int fBaseWeight;
+
+ // resolved style actually used for rendering
FontStyle fStyle;
};
@@ -41,6 +48,8 @@
TypefaceImpl* TypefaceImpl_createFromTypeface(TypefaceImpl* src, SkTypeface::Style style);
+TypefaceImpl* TypefaceImpl_createWeightAlias(TypefaceImpl* src, int baseweight);
+
// When we remove the USE_MINIKIN ifdef, probably a good idea to move the casting
// (from jlong to FontFamily*) to the caller in Typeface.cpp.
TypefaceImpl* TypefaceImpl_createFromFamilies(const jlong* families, size_t size);
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 0f0281b..c4a949f 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
+ NOTE: this file is the legacy format, for compatibility with apps. The new,
+ more flexible format is fonts.xml. Please keep the two in sync until the legacy
+ format can be fully removed.
+
Fallback Fonts
This file specifies the fonts, and the priority order, that will be searched for any
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index a4d4906..04bc67a 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -1,4 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ NOTE: this is the newer (L) version of the system font configuration,
+ supporting richer weight selection. Some apps will expect the older
+ version, so please keep system_fonts.xml and fallback_fonts.xml in sync
+ with any changes, even though framework will only read this file.
+
+ All fonts withohut names are added to the default list. Fonts are chosen
+ based on a match: full BCP-47 language tag including script, then just
+ language, and finally order (the first font containing the glyph).
+
+ Order of appearance is also the tiebreaker for weight matching. This is
+ the reason why the 900 weights of Roboto precede the 700 weights - we
+ prefer the former when an 800 weight is requested. Since bold spans
+ effectively add 300 to the weight, this ensures that 900 is the bold
+ paired with the 500 weight, ensuring adequate contrast.
+-->
<familyset version="22">
<!-- first font is default -->
<family name="sans-serif">
@@ -10,15 +26,16 @@
<font weight="400" style="italic">Roboto-Italic.ttf</font>
<font weight="500" style="normal">Roboto-Medium.ttf</font>
<font weight="500" style="italic">Roboto-MediumItalic.ttf</font>
- <font weight="700" style="normal">Roboto-Bold.ttf</font>
- <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
<font weight="900" style="normal">Roboto-Black.ttf</font>
<font weight="900" style="italic">Roboto-BlackItalic.ttf</font>
+ <font weight="700" style="normal">Roboto-Bold.ttf</font>
+ <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
</family>
<!-- Note that aliases must come after the fonts they reference. -->
<alias name="sans-serif-thin" to="sans-serif" weight="100" />
<alias name="sans-serif-light" to="sans-serif" weight="300" />
+ <alias name="sans-serif-medium" to="sans-serif" weight="500" />
<alias name="sans-serif-black" to="sans-serif" weight="900" />
<alias name="arial" to="sans-serif" />
<alias name="helvetica" to="sans-serif" />
@@ -227,6 +244,9 @@
<family>
<font weight="400" style="normal">NotoColorEmoji.ttf</font>
</family>
+ <family>
+ <font weight="400" style="normal">DroidSansFallback.ttf</font>
+ </family>
<family lang="ja">
<font weight="400" style="normal">MTLmr3m.ttf</font>
</family>
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 11d3165..b8b7e76 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -18,8 +18,6 @@
import android.content.res.AssetManager;
-import java.io.File;
-
/**
* A family of typefaces with different styles.
*
@@ -64,6 +62,10 @@
return nAddFont(mNativePtr, path);
}
+ public boolean addFontWeightStyle(String path, int weight, boolean style) {
+ return nAddFontWeightStyle(mNativePtr, path, weight, style);
+ }
+
public boolean addFontFromAsset(AssetManager mgr, String path) {
return nAddFontFromAsset(mNativePtr, mgr, path);
}
@@ -71,6 +73,8 @@
private static native long nCreateFamily(String lang, int variant);
private static native void nUnrefFamily(long nativePtr);
private static native boolean nAddFont(long nativeFamily, String path);
+ private static native boolean nAddFontWeightStyle(long nativeFamily, String path,
+ int weight, boolean isItalic);
private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
String path);
}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index a863a06..97081f9 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -33,23 +33,48 @@
*/
public class FontListParser {
+ public static class Config {
+ Config() {
+ families = new ArrayList<Family>();
+ aliases = new ArrayList<Alias>();
+ }
+ public List<Family> families;
+ public List<Alias> aliases;
+ }
+
+ public static class Font {
+ Font(String fontName, int weight, boolean isItalic) {
+ this.fontName = fontName;
+ this.weight = weight;
+ this.isItalic = isItalic;
+ }
+ public String fontName;
+ public int weight;
+ public boolean isItalic;
+ }
+
+ public static class Alias {
+ public String name;
+ public String toName;
+ public int weight;
+ }
+
public static class Family {
- public Family(List<String> names, List<String> fontFiles, String lang, String variant) {
- this.names = names;
- this.fontFiles = fontFiles;
+ public Family(String name, List<Font> fonts, String lang, String variant) {
+ this.name = name;
+ this.fonts = fonts;
this.lang = lang;
this.variant = variant;
}
- public List<String> names;
- // todo: need attributes for font files
- public List<String> fontFiles;
+ public String name;
+ public List<Font> fonts;
public String lang;
public String variant;
}
/* Parse fallback list (no names) */
- public static List<Family> parse(InputStream in) throws XmlPullParserException, IOException {
+ public static Config parse(InputStream in) throws XmlPullParserException, IOException {
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
@@ -60,57 +85,59 @@
}
}
- private static List<Family> readFamilies(XmlPullParser parser)
+ private static Config readFamilies(XmlPullParser parser)
throws XmlPullParserException, IOException {
- List<Family> families = new ArrayList<Family>();
+ Config config = new Config();
parser.require(XmlPullParser.START_TAG, null, "familyset");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
if (parser.getName().equals("family")) {
- families.add(readFamily(parser));
+ config.families.add(readFamily(parser));
+ } else if (parser.getName().equals("alias")) {
+ config.aliases.add(readAlias(parser));
} else {
skip(parser);
}
}
- return families;
+ return config;
}
private static Family readFamily(XmlPullParser parser)
throws XmlPullParserException, IOException {
- List<String> names = null;
- List<String> fontFiles = new ArrayList<String>();
- String lang = null;
- String variant = null;
+ String name = parser.getAttributeValue(null, "name");
+ String lang = parser.getAttributeValue(null, "lang");
+ String variant = parser.getAttributeValue(null, "variant");
+ List<Font> fonts = new ArrayList<Font>();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
- if (tag.equals("fileset")) {
- while (parser.next() != XmlPullParser.END_TAG) {
- if (parser.getEventType() != XmlPullParser.START_TAG) continue;
- if (parser.getName().equals("file")) {
- if (lang == null) {
- lang = parser.getAttributeValue(null, "lang");
- }
- if (variant == null) {
- variant = parser.getAttributeValue(null, "variant");
- }
- String filename = parser.nextText();
- String fullFilename = "/system/fonts/" + filename;
- fontFiles.add(fullFilename);
- }
- }
- } else if (tag.equals("nameset")) {
- names = new ArrayList<String>();
- while (parser.next() != XmlPullParser.END_TAG) {
- if (parser.getEventType() != XmlPullParser.START_TAG) continue;
- if (parser.getName().equals("name")) {
- String name = parser.nextText();
- names.add(name);
- }
- }
+ if (tag.equals("font")) {
+ String weightStr = parser.getAttributeValue(null, "weight");
+ int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
+ boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
+ String filename = parser.nextText();
+ String fullFilename = "/system/fonts/" + filename;
+ fonts.add(new Font(fullFilename, weight, isItalic));
+ } else {
+ skip(parser);
}
}
- return new Family(names, fontFiles, lang, variant);
+ return new Family(name, fonts, lang, variant);
+ }
+
+ private static Alias readAlias(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ Alias alias = new Alias();
+ alias.name = parser.getAttributeValue(null, "name");
+ alias.toName = parser.getAttributeValue(null, "to");
+ String weightStr = parser.getAttributeValue(null, "weight");
+ if (weightStr == null) {
+ alias.weight = 400;
+ } else {
+ alias.weight = Integer.parseInt(weightStr);
+ }
+ skip(parser); // alias tag is empty, ignore any contents and consume end tag
+ return alias;
}
private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 568d3f2..db42314 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -66,8 +66,7 @@
static Map<String, Typeface> sSystemFontMap;
static FontFamily[] sFallbackFonts;
- static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
- static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
+ static final String FONTS_CONFIG = "fonts.xml";
/**
* @hide
@@ -261,10 +260,9 @@
}
private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
- // TODO: expand to handle attributes like lang and variant
FontFamily fontFamily = new FontFamily(family.lang, family.variant);
- for (String fontFile : family.fontFiles) {
- fontFamily.addFont(fontFile);
+ for (FontListParser.Font font : family.fonts) {
+ fontFamily.addFontWeightStyle(font.fontName, font.weight, font.isItalic);
}
return fontFamily;
}
@@ -277,48 +275,53 @@
private static void init() {
// Load font config and initialize Minikin state
File systemFontConfigLocation = getSystemFontConfigLocation();
- File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
- File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
+ File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
try {
- // TODO: throws an exception non-Minikin builds, to fail early;
- // remove when Minikin-only
- new FontFamily();
+ FileInputStream fontsIn = new FileInputStream(configFilename);
+ FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
- FileInputStream systemIn = new FileInputStream(systemConfigFilename);
- List<FontListParser.Family> systemFontConfig = FontListParser.parse(systemIn);
-
- FileInputStream fallbackIn = new FileInputStream(configFilename);
List<FontFamily> familyList = new ArrayList<FontFamily>();
// Note that the default typeface is always present in the fallback list;
// this is an enhancement from pre-Minikin behavior.
- familyList.add(makeFamilyFromParsed(systemFontConfig.get(0)));
- for (Family f : FontListParser.parse(fallbackIn)) {
- familyList.add(makeFamilyFromParsed(f));
+ for (int i = 0; i < fontConfig.families.size(); i++) {
+ Family f = fontConfig.families.get(i);
+ if (i == 0 || f.name == null) {
+ familyList.add(makeFamilyFromParsed(f));
+ }
}
sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
setDefault(Typeface.createFromFamilies(sFallbackFonts));
Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
- for (int i = 0; i < systemFontConfig.size(); i++) {
+ for (int i = 0; i < fontConfig.families.size(); i++) {
Typeface typeface;
- Family f = systemFontConfig.get(i);
- if (i == 0) {
- // The first entry is the default typeface; no sense in duplicating
- // the corresponding FontFamily.
- typeface = sDefaultTypeface;
- } else {
- FontFamily fontFamily = makeFamilyFromParsed(f);
- FontFamily[] families = { fontFamily };
- typeface = Typeface.createFromFamiliesWithDefault(families);
+ Family f = fontConfig.families.get(i);
+ if (f.name != null) {
+ if (i == 0) {
+ // The first entry is the default typeface; no sense in
+ // duplicating the corresponding FontFamily.
+ typeface = sDefaultTypeface;
+ } else {
+ FontFamily fontFamily = makeFamilyFromParsed(f);
+ FontFamily[] families = { fontFamily };
+ typeface = Typeface.createFromFamiliesWithDefault(families);
+ }
+ systemFonts.put(f.name, typeface);
}
- for (String name : f.names) {
- systemFonts.put(name, typeface);
+ }
+ for (FontListParser.Alias alias : fontConfig.aliases) {
+ Typeface base = systemFonts.get(alias.toName);
+ Typeface newFace = base;
+ int weight = alias.weight;
+ if (weight != 400) {
+ newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
}
+ systemFonts.put(alias.name, newFace);
}
sSystemFontMap = systemFonts;
} catch (RuntimeException e) {
- Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)");
+ Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
// TODO: normal in non-Minikin case, remove or make error when Minikin-only
} catch (FileNotFoundException e) {
Log.e(TAG, "Error opening " + configFilename);
@@ -383,6 +386,7 @@
}
private static native long nativeCreateFromTypeface(long native_instance, int style);
+ private static native long nativeCreateWeightAlias(long native_instance, int weight);
private static native void nativeUnref(long native_instance);
private static native int nativeGetStyle(long native_instance);
private static native long nativeCreateFromArray(long[] familyArray);