diff options
| author | 2015-11-06 11:57:09 -0500 | |
|---|---|---|
| committer | 2016-02-12 16:28:35 +0000 | |
| commit | a87b07d7fafd59ae26073a80cd742b17ea427ecd (patch) | |
| tree | 22214c42f3245b6113f79315c5eb45ad98e72ae6 | |
| parent | 67a28e8c3809f7d68999f586edd7d7ff4bd27c9b (diff) | |
Add support for gx font variation axes.
This adds an 'axis' child element to the 'font' element. The 'axis'
element has attributes 'tag' (a four byte identifier) and 'stylevalue'
(a float value) to the parser. This also modifies reading the font file
name in a backwards compatible fashion by using only the direct #text
children of the 'font' element. (Both the Minikin and Skia parsers now
allow the font file name on a separate line in the fonts.xml file).
This information is then passed through to Skia in order to select the
desired variation. The Skia parser already parses this way and has for
some time, so Chrome and WebView can already read this format.
Change-Id: I15623fe864fa92b2bf0705af5e389daedfb77e5c
(cherry picked from commit b8e367fb7428076ff2e4aa2a97adaed1ef806e92)
| -rw-r--r-- | core/jni/android/graphics/FontFamily.cpp | 71 | ||||
| -rw-r--r-- | graphics/java/android/graphics/FontFamily.java | 10 | ||||
| -rw-r--r-- | graphics/java/android/graphics/FontListParser.java | 92 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Typeface.java | 14 | ||||
| -rw-r--r-- | tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java | 4 |
5 files changed, 162 insertions, 29 deletions
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 7c8dbe8ecf23..149ff557645d 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -20,6 +20,7 @@ #include <core_jni_helpers.h> #include "SkData.h" +#include "SkFontMgr.h" #include "SkRefCnt.h" #include "SkTypeface.h" #include "GraphicsJNI.h" @@ -33,6 +34,8 @@ #include <minikin/FontFamily.h> #include "MinikinSkia.h" +#include <memory> + namespace android { static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) { @@ -69,13 +72,56 @@ static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, return addSkTypeface(fontFamily, face); } +static struct { + jmethodID mGet; + jmethodID mSize; +} gListClassInfo; + +static struct { + jfieldID mTag; + jfieldID mStyleValue; +} gAxisClassInfo; + static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr, - jstring path, jint ttcIndex, jint weight, jboolean isItalic) { + jstring path, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) { NPE_CHECK_RETURN_ZERO(env, path); + + // Declare axis native type. + std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes; + int skiaAxesLength = 0; + if (listOfAxis) { + jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize); + + skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]); + skiaAxesLength = listSize; + for (jint i = 0; i < listSize; ++i) { + jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i); + if (!axisObject) { + skiaAxes[i].fTag = 0; + skiaAxes[i].fStyleValue = 0; + continue; + } + + jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag); + jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue); + skiaAxes[i].fTag = tag; + skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue); + } + } + ScopedUtfChars str(env, path); - SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex); + SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); + std::unique_ptr<SkStreamAsset> fontData(SkStream::NewFromFile(str.c_str())); + if (!fontData) { + ALOGE("addFont failed to open %s", str.c_str()); + return false; + } + SkFontMgr::FontParameters params; + params.setCollectionIndex(ttcIndex); + params.setAxes(skiaAxes.get(), skiaAxesLength); + SkTypeface* face = fm->createFromStream(fontData.release(), params); if (face == NULL) { - ALOGE("addFont failed to create font %s", str.c_str()); + ALOGE("addFont failed to create font %s#%d", str.c_str(), ttcIndex); return false; } FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); @@ -129,15 +175,26 @@ static const JNINativeMethod gFontFamilyMethods[] = { { "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create }, { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, { "nAddFont", "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont }, - { "nAddFontWeightStyle", "(JLjava/lang/String;IIZ)Z", (void*)FontFamily_addFontWeightStyle }, + { "nAddFontWeightStyle", "(JLjava/lang/String;ILjava/util/List;IZ)Z", + (void*)FontFamily_addFontWeightStyle }, { "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z", - (void*)FontFamily_addFontFromAsset }, + (void*)FontFamily_addFontFromAsset }, }; int register_android_graphics_FontFamily(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods, - NELEM(gFontFamilyMethods)); + int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods, + NELEM(gFontFamilyMethods)); + + jclass listClass = FindClassOrDie(env, "java/util/List"); + gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;"); + gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I"); + + jclass axisClass = FindClassOrDie(env, "android/graphics/FontListParser$Axis"); + gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I"); + gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F"); + + return err; } } diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index 6309ed36eb51..3897c3bf45d8 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -18,6 +18,8 @@ package android.graphics; import android.content.res.AssetManager; +import java.util.List; + /** * A family of typefaces with different styles. * @@ -62,8 +64,9 @@ public class FontFamily { return nAddFont(mNativePtr, path, ttcIndex); } - public boolean addFontWeightStyle(String path, int ttcIndex, int weight, boolean style) { - return nAddFontWeightStyle(mNativePtr, path, ttcIndex, weight, style); + public boolean addFontWeightStyle(String path, int ttcIndex, List<FontListParser.Axis> axes, + int weight, boolean style) { + return nAddFontWeightStyle(mNativePtr, path, ttcIndex, axes, weight, style); } public boolean addFontFromAsset(AssetManager mgr, String path) { @@ -74,7 +77,8 @@ public class FontFamily { private static native void nUnrefFamily(long nativePtr); private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex); private static native boolean nAddFontWeightStyle(long nativeFamily, String path, - int ttcIndex, int weight, boolean isItalic); + int ttcIndex, List<FontListParser.Axis> listOfAxis, + 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 774f6b8922e0..8f7c6a62a4a3 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; /** * Parser for font config files. @@ -42,15 +43,26 @@ public class FontListParser { public List<Alias> aliases; } + public static class Axis { + Axis(int tag, float styleValue) { + this.tag = tag; + this.styleValue = styleValue; + } + public final int tag; + public final float styleValue; + } + public static class Font { - Font(String fontName, int ttcIndex, int weight, boolean isItalic) { + Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) { this.fontName = fontName; this.ttcIndex = ttcIndex; + this.axes = axes; this.weight = weight; this.isItalic = isItalic; } public String fontName; public int ttcIndex; + public final List<Axis> axes; public int weight; public boolean isItalic; } @@ -93,9 +105,10 @@ public class FontListParser { 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")) { + String tag = parser.getName(); + if (tag.equals("family")) { config.families.add(readFamily(parser)); - } else if (parser.getName().equals("alias")) { + } else if (tag.equals("alias")) { config.aliases.add(readAlias(parser)); } else { skip(parser); @@ -114,14 +127,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("font")) { - String ttcIndexStr = parser.getAttributeValue(null, "index"); - int ttcIndex = ttcIndexStr == null ? 0 : Integer.parseInt(ttcIndexStr); - 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, ttcIndex, weight, isItalic)); + fonts.add(readFont(parser)); } else { skip(parser); } @@ -129,6 +135,70 @@ public class FontListParser { return new Family(name, fonts, lang, variant); } + /** Matches leading and trailing XML whitespace. */ + private static final Pattern FILENAME_WHITESPACE_PATTERN = + Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); + + private static Font readFont(XmlPullParser parser) + throws XmlPullParserException, IOException { + String indexStr = parser.getAttributeValue(null, "index"); + int index = indexStr == null ? 0 : Integer.parseInt(indexStr); + List<Axis> axes = new ArrayList<Axis>(); + String weightStr = parser.getAttributeValue(null, "weight"); + int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); + boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); + StringBuilder filename = new StringBuilder(); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() == XmlPullParser.TEXT) { + filename.append(parser.getText()); + } + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + String tag = parser.getName(); + if (tag.equals("axis")) { + axes.add(readAxis(parser)); + } else { + skip(parser); + } + } + String fullFilename = "/system/fonts/" + + FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); + return new Font(fullFilename, index, axes, weight, isItalic); + } + + /** The 'tag' attribute value is read as four character values between 0 and 255 inclusive. */ + private static final Pattern TAG_PATTERN = Pattern.compile("[\\x00-\\xFF]{4}"); + + /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>', + * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9]. + */ + private static final Pattern STYLE_VALUE_PATTERN = + Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))"); + + private static Axis readAxis(XmlPullParser parser) + throws XmlPullParserException, IOException { + int tag = 0; + String tagStr = parser.getAttributeValue(null, "tag"); + if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) { + tag = tagStr.charAt(0) << 24 + + tagStr.charAt(1) << 16 + + tagStr.charAt(2) << 8 + + tagStr.charAt(3); + } else { + throw new XmlPullParserException("Invalid tag attribute value.", parser, null); + } + + float styleValue = 0; + String styleValueStr = parser.getAttributeValue(null, "stylevalue"); + if (styleValueStr != null && STYLE_VALUE_PATTERN.matcher(styleValueStr).matches()) { + styleValue = Float.parseFloat(styleValueStr); + } else { + throw new XmlPullParserException("Invalid styleValue attribute value.", parser, null); + } + + skip(parser); // axis tag is empty, ignore any contents and consume end tag + return new Axis(tag, styleValue); + } + private static Alias readAlias(XmlPullParser parser) throws XmlPullParserException, IOException { Alias alias = new Alias(); diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 1294323ce69b..345f257c9371 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -17,7 +17,6 @@ package android.graphics; import android.content.res.AssetManager; -import android.graphics.FontListParser.Family; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; @@ -262,7 +261,8 @@ public class Typeface { private static FontFamily makeFamilyFromParsed(FontListParser.Family family) { FontFamily fontFamily = new FontFamily(family.lang, family.variant); for (FontListParser.Font font : family.fonts) { - fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.weight, font.isItalic); + fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.axes, + font.weight, font.isItalic); } return fontFamily; } @@ -284,7 +284,7 @@ public class Typeface { // Note that the default typeface is always present in the fallback list; // this is an enhancement from pre-Minikin behavior. for (int i = 0; i < fontConfig.families.size(); i++) { - Family f = fontConfig.families.get(i); + FontListParser.Family f = fontConfig.families.get(i); if (i == 0 || f.name == null) { familyList.add(makeFamilyFromParsed(f)); } @@ -295,7 +295,7 @@ public class Typeface { Map<String, Typeface> systemFonts = new HashMap<String, Typeface>(); for (int i = 0; i < fontConfig.families.size(); i++) { Typeface typeface; - Family f = fontConfig.families.get(i); + FontListParser.Family f = fontConfig.families.get(i); if (f.name != null) { if (i == 0) { // The first entry is the default typeface; no sense in @@ -324,11 +324,11 @@ public class Typeface { 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); + Log.e(TAG, "Error opening " + configFilename, e); } catch (IOException e) { - Log.e(TAG, "Error reading " + configFilename); + Log.e(TAG, "Error reading " + configFilename, e); } catch (XmlPullParserException e) { - Log.e(TAG, "XML parse exception for " + configFilename); + Log.e(TAG, "XML parse exception for " + configFilename, e); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java index c7b24bcb352d..b6588b65b5ae 100644 --- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java @@ -288,8 +288,10 @@ public class FontFamily_Delegate { } @LayoutlibDelegate - /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path, + /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, + final String path, final int index, final List<FontListParser.Axis> axes, final int weight, final boolean isItalic) { + // 'index' and 'axes' are not supported by java.awt.Font final FontFamily_Delegate delegate = getDelegate(nativeFamily); if (delegate != null) { if (sFontLocation == null) { |