From 117cbebe810613d4a6de034f02652cdbbfef4cde Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 25 Aug 2014 13:47:16 -0700 Subject: New weight-aware font config Parse new fonts.xml config file, and resolve weight selection based on the base weight of the font (as defined by a weight alias specified in the config file) and the requested bold flag. This change improves the appearance of bold spans for alternate weights of Roboto. In addition, this patch enables weight selection for fallback fonts. For example, if an additional font with a weight of 100 is added to the Hebrew font family in the fallback list, then requesting "sans-serif-thin" would select that font for Hebrew text. Bug: 14538154 Change-Id: I99a04fad4f7bf01c75726e760d42735dd9003496 --- graphics/java/android/graphics/FontFamily.java | 8 +- graphics/java/android/graphics/FontListParser.java | 107 +++++++++++++-------- graphics/java/android/graphics/Typeface.java | 64 ++++++------ 3 files changed, 107 insertions(+), 72 deletions(-) (limited to 'graphics/java/android') diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index 11d3165fdc29..b8b7e76e3b20 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -18,8 +18,6 @@ package android.graphics; import android.content.res.AssetManager; -import java.io.File; - /** * A family of typefaces with different styles. * @@ -64,6 +62,10 @@ public class FontFamily { 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 @@ public class FontFamily { 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 a863a06121fc..97081f9c77fd 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -33,23 +33,48 @@ import java.util.List; */ public class FontListParser { + public static class Config { + Config() { + families = new ArrayList(); + aliases = new ArrayList(); + } + public List families; + public List 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 names, List fontFiles, String lang, String variant) { - this.names = names; - this.fontFiles = fontFiles; + public Family(String name, List fonts, String lang, String variant) { + this.name = name; + this.fonts = fonts; this.lang = lang; this.variant = variant; } - public List names; - // todo: need attributes for font files - public List fontFiles; + public String name; + public List fonts; public String lang; public String variant; } /* Parse fallback list (no names) */ - public static List 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 @@ public class FontListParser { } } - private static List readFamilies(XmlPullParser parser) + private static Config readFamilies(XmlPullParser parser) throws XmlPullParserException, IOException { - List families = new ArrayList(); + 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 names = null; - List fontFiles = new ArrayList(); - 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 fonts = new ArrayList(); 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(); - 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 568d3f2aad4e..db42314447ff 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -66,8 +66,7 @@ public class Typeface { static Map 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 @@ public class Typeface { } 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 @@ public class Typeface { 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 systemFontConfig = FontListParser.parse(systemIn); - - FileInputStream fallbackIn = new FileInputStream(configFilename); List familyList = new ArrayList(); // 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 systemFonts = new HashMap(); - 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 @@ public class Typeface { } 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); -- cgit v1.2.3-59-g8ed1b