diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 3ce2589698224..6fdb71fcbab0c 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; import android.graphics.fonts.Font; +import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.support.test.InstrumentationRegistry; @@ -36,12 +37,15 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; @@ -62,6 +66,8 @@ public class TypefaceSystemFallbackTest { }; private static final String TEST_FONTS_XML; private static final String TEST_FONT_DIR; + private static final String TEST_OEM_XML; + private static final String TEST_OEM_DIR; private static final float GLYPH_1EM_WIDTH; private static final float GLYPH_2EM_WIDTH; @@ -73,8 +79,13 @@ public class TypefaceSystemFallbackTest { if (!cacheDir.isDirectory()) { cacheDir.mkdirs(); } - TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/"; + TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/fonts/"; TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath(); + TEST_OEM_DIR = cacheDir.getAbsolutePath() + "/oem_fonts/"; + TEST_OEM_XML = new File(cacheDir, "fonts_customization.xml").getAbsolutePath(); + + new File(TEST_FONT_DIR).mkdirs(); + new File(TEST_OEM_DIR).mkdirs(); final AssetManager am = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); @@ -99,6 +110,12 @@ public class TypefaceSystemFallbackTest { } catch (IOException e) { throw new RuntimeException(e); } + final File outOemInCache = new File(TEST_OEM_DIR, fontFile); + try (InputStream is = am.open(sourceInAsset)) { + Files.copy(is, outOemInCache.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @@ -107,11 +124,14 @@ public class TypefaceSystemFallbackTest { for (final String fontFile : TEST_FONT_FILES) { final File outInCache = new File(TEST_FONT_DIR, fontFile); outInCache.delete(); + final File outOemInCache = new File(TEST_OEM_DIR, fontFile); + outInCache.delete(); } } private static void buildSystemFallback(String xml, - ArrayMap fontMap, ArrayMap fallbackMap) { + FontCustomizationParser.Result oemCustomization, ArrayMap fontMap, + ArrayMap fallbackMap) { final ArrayList availableFonts = new ArrayList<>(); try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { fos.write(xml.getBytes(Charset.forName("UTF-8"))); @@ -119,18 +139,28 @@ public class TypefaceSystemFallbackTest { throw new RuntimeException(e); } final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, fallbackMap, availableFonts); + TEST_FONT_DIR, oemCustomization, fallbackMap, availableFonts); Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); } + private static FontCustomizationParser.Result readFontCustomization(String oemXml) { + try (InputStream is = new ByteArrayInputStream(oemXml.getBytes(StandardCharsets.UTF_8))) { + return FontCustomizationParser.parse(is, TEST_OEM_DIR); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + } + @Test public void testBuildSystemFallback() { final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); final ArrayList availableFonts = new ArrayList<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, - SYSTEM_FONT_DIR, fallbackMap, availableFonts); + SYSTEM_FONT_DIR, oemCustomization, fallbackMap, availableFonts); assertNotNull(aliases); assertFalse(fallbackMap.isEmpty()); @@ -156,8 +186,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); assertEquals(1, fontMap.size()); assertTrue(fontMap.containsKey("sans-serif")); @@ -184,8 +216,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -230,8 +264,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -275,8 +311,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -325,8 +363,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -371,8 +411,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -410,8 +452,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -449,8 +493,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -497,8 +543,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); paint.setTypeface(fontMap.get("sans-serif")); @@ -539,8 +587,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -578,8 +628,10 @@ public class TypefaceSystemFallbackTest { + ""; final ArrayMap fontMap = new ArrayMap<>(); final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -598,4 +650,191 @@ public class TypefaceSystemFallbackTest { assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); } + @Test + public void testBuildSystemFallback__Customization_new_named_family() { + final String xml = "" + + "" + + " " + + " a3em.ttf" + + " " + + ""; + final String oemXml = "" + + "" + + " " + + " b3em.ttf" + + " " + + ""; + final ArrayMap fontMap = new ArrayMap<>(); + final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("google-sans"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback__Customization_new_named_family_override() { + final String xml = "" + + "" + + " " + + " a3em.ttf" + + " " + + ""; + final String oemXml = "" + + "" + + " " + + " b3em.ttf" + + " " + + ""; + final ArrayMap fontMap = new ArrayMap<>(); + final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback__Customization_additional_alias() { + final String xml = "" + + "" + + " " + + " a3em.ttf" + + " " + + ""; + final String oemXml = "" + + "" + + " " + + " b3em.ttf" + + " c3em.ttf" + + " " + + " " + + " " + + ""; + final ArrayMap fontMap = new ArrayMap<>(); + final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("google-sans"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("another-google-sans"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("google-sans-bold"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback__Customization_additional_alias_conflict_with_new_name() { + final String xml = "" + + "" + + " " + + " a3em.ttf" + + " " + + " " + + ""; + final String oemXml = "" + + "" + + " " + + " b3em.ttf" + + " " + + ""; + final ArrayMap fontMap = new ArrayMap<>(); + final ArrayMap fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("named-family"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("named-alias"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test(expected = IllegalArgumentException.class) + public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() { + final String oemXml = "" + + "" + + " " + + " b3em.ttf" + + " " + + ""; + readFontCustomization(oemXml); + } + + @Test(expected = IllegalArgumentException.class) + public void testBuildSystemFallback__Customization_new_named_family_dup_name_exception() { + final String oemXml = "" + + "" + + " " + + " b3em.ttf" + + " " + + " " + + " b3em.ttf" + + " " + + ""; + readFontCustomization(oemXml); + } } diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index 898e78c651e68..5592aac5a7c0a 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.AssetManager; import android.graphics.Typeface; import android.graphics.fonts.Font; +import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.support.test.InstrumentationRegistry; @@ -77,8 +78,10 @@ public class FontFallbackSetup implements AutoCloseable { final ArrayMap fallbackMap = new ArrayMap<>(); final ArrayList availableFonts = new ArrayList<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, - mTestFontsDir, fallbackMap, availableFonts); + mTestFontsDir, oemCustomization, fallbackMap, availableFonts); Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); } diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index 76eb4e6769235..454dceb9c82c5 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -89,23 +89,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := fonts.xml LOCAL_MODULE_CLASS := ETC - -AOSP_FONTS_FILE := frameworks/base/data/fonts/fonts.xml - -ifdef ADDITIONAL_FONTS_FILE -ADDITIONAL_FONTS_SCRIPT := frameworks/base/tools/fonts/add_additional_fonts.py -ADD_ADDITIONAL_FONTS := $(local-generated-sources-dir)/fonts.xml - -$(ADD_ADDITIONAL_FONTS): PRIVATE_SCRIPT := $(ADDITIONAL_FONTS_SCRIPT) -$(ADD_ADDITIONAL_FONTS): PRIVATE_ADDITIONAL_FONTS_FILE := $(ADDITIONAL_FONTS_FILE) -$(ADD_ADDITIONAL_FONTS): $(ADDITIONAL_FONTS_SCRIPT) $(AOSP_FONTS_FILE) $(ADDITIONAL_FONTS_FILE) - rm -f $@ - python $(PRIVATE_SCRIPT) $@ $(PRIVATE_ADDITIONAL_FONTS_FILE) -else -ADD_ADDITIONAL_FONTS := $(AOSP_FONTS_FILE) -endif - -LOCAL_PREBUILT_MODULE_FILE := $(ADD_ADDITIONAL_FONTS) +LOCAL_PREBUILT_MODULE_FILE := frameworks/base/data/fonts/fonts.xml include $(BUILD_PREBUILT) diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 82435d5511f57..21cc3757a40eb 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -40,17 +40,25 @@ public class FontListParser { /* Parse fallback list (no names) */ @UnsupportedAppUsage public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { + return parse(in, "/system/fonts"); + } + + /** + * Parse the fonts.xml + */ + public static FontConfig parse(InputStream in, String fontDir) + throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser); + return readFamilies(parser, fontDir); } finally { in.close(); } } - private static FontConfig readFamilies(XmlPullParser parser) + private static FontConfig readFamilies(XmlPullParser parser, String fontDir) throws XmlPullParserException, IOException { List families = new ArrayList<>(); List aliases = new ArrayList<>(); @@ -60,7 +68,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - families.add(readFamily(parser)); + families.add(readFamily(parser, fontDir)); } else if (tag.equals("alias")) { aliases.add(readAlias(parser)); } else { @@ -71,7 +79,10 @@ public class FontListParser { aliases.toArray(new FontConfig.Alias[aliases.size()])); } - private static FontConfig.Family readFamily(XmlPullParser parser) + /** + * Reads a family element + */ + public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir) throws XmlPullParserException, IOException { final String name = parser.getAttributeValue(null, "name"); final String lang = parser.getAttributeValue("", "lang"); @@ -81,7 +92,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; final String tag = parser.getName(); if (tag.equals("font")) { - fonts.add(readFont(parser)); + fonts.add(readFont(parser, fontDir)); } else { skip(parser); } @@ -102,7 +113,7 @@ public class FontListParser { private static final Pattern FILENAME_WHITESPACE_PATTERN = Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); - private static FontConfig.Font readFont(XmlPullParser parser) + private static FontConfig.Font readFont(XmlPullParser parser, String fontDir) throws XmlPullParserException, IOException { String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); @@ -125,7 +136,7 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - return new FontConfig.Font(sanitizedName, index, axes.toArray( + return new FontConfig.Font(fontDir + sanitizedName, index, axes.toArray( new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); } @@ -137,7 +148,10 @@ public class FontListParser { return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr)); } - private static FontConfig.Alias readAlias(XmlPullParser parser) + /** + * Reads alias elements + */ + public static FontConfig.Alias readAlias(XmlPullParser parser) throws XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); String toName = parser.getAttributeValue(null, "to"); @@ -152,7 +166,10 @@ public class FontListParser { return new FontConfig.Alias(name, toName, weight); } - private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + /** + * Skip until next element + */ + public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { int depth = 1; while (depth > 0) { switch (parser.next()) { diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index e6ac06011e230..7ad207f339d11 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1103,6 +1103,9 @@ public class Typeface { } for (FontConfig.Alias alias : aliases) { + if (systemFontMap.containsKey(alias.getName())) { + continue; // If alias and named family are conflict, use named family. + } final Typeface base = systemFontMap.get(alias.getToName()); final int weight = alias.getWeight(); final Typeface newFace = weight == 400 ? base : diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java new file mode 100644 index 0000000000000..0291d7484dc5f --- /dev/null +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.fonts; + +import android.annotation.NonNull; +import android.graphics.FontListParser; +import android.text.FontConfig; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; + +/** + * Parser for font customization + * + * @hide + */ +public class FontCustomizationParser { + /** + * Represents a customization XML + */ + public static class Result { + ArrayList mAdditionalNamedFamilies = new ArrayList<>(); + ArrayList mAdditionalAliases = new ArrayList<>(); + } + + /** + * Parses the customization XML + * + * Caller must close the input stream + */ + public static Result parse(@NonNull InputStream in, @NonNull String fontDir) + throws XmlPullParserException, IOException { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser, fontDir); + } + + private static void validate(Result result) { + HashSet familyNames = new HashSet<>(); + for (int i = 0; i < result.mAdditionalNamedFamilies.size(); ++i) { + final FontConfig.Family family = result.mAdditionalNamedFamilies.get(i); + final String name = family.getName(); + if (name == null) { + throw new IllegalArgumentException("new-named-family requires name attribute"); + } + if (!familyNames.add(name)) { + throw new IllegalArgumentException( + "new-named-family requires unique name attribute"); + } + } + } + + private static Result readFamilies(XmlPullParser parser, String fontDir) + throws XmlPullParserException, IOException { + Result out = new Result(); + parser.require(XmlPullParser.START_TAG, null, "fonts-modification"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + String tag = parser.getName(); + if (tag.equals("family")) { + readFamily(parser, fontDir, out); + } else if (tag.equals("alias")) { + out.mAdditionalAliases.add(FontListParser.readAlias(parser)); + } else { + FontListParser.skip(parser); + } + } + validate(out); + return out; + } + + private static void readFamily(XmlPullParser parser, String fontDir, Result out) + throws XmlPullParserException, IOException { + final String customizationType = parser.getAttributeValue(null, "customizationType"); + if (customizationType == null) { + throw new IllegalArgumentException("customizationType must be specified"); + } + if (customizationType.equals("new-named-family")) { + out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir)); + } else { + throw new IllegalArgumentException("Unknown customizationType=" + customizationType); + } + } +} diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 5e80749e92330..2d21bbbd4e43e 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -113,7 +113,6 @@ public final class SystemFonts { private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily, @NonNull ArrayMap> fallbackMap, @NonNull Map cache, - @NonNull String fontDir, @NonNull ArrayList availableFonts) { final String languageTags = xmlFamily.getLanguages(); @@ -138,8 +137,7 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir, - availableFonts); + xmlFamily.getName(), defaultFonts, languageTags, variant, cache, availableFonts); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { @@ -151,7 +149,7 @@ public final class SystemFonts { } } else { final FontFamily family = createFontFamily( - xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir, + xmlFamily.getName(), fallback, languageTags, variant, cache, availableFonts); if (family != null) { fallbackMap.valueAt(i).add(family); @@ -169,7 +167,6 @@ public final class SystemFonts { @NonNull String languageTags, @FontConfig.Family.Variant int variant, @NonNull Map cache, - @NonNull String fontDir, @NonNull ArrayList availableFonts) { if (fonts.size() == 0) { return null; @@ -178,7 +175,7 @@ public final class SystemFonts { FontFamily.Builder b = null; for (int i = 0; i < fonts.size(); i++) { final FontConfig.Font fontConfig = fonts.get(i); - final String fullPath = fontDir + fontConfig.getFontName(); + final String fullPath = fontConfig.getFontName(); ByteBuffer buffer = cache.get(fullPath); if (buffer == null) { if (cache.containsKey(fullPath)) { @@ -213,6 +210,22 @@ public final class SystemFonts { return b == null ? null : b.build(languageTags, variant); } + private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, + @NonNull HashMap bufferCache, + @NonNull ArrayMap> fallbackListMap, + @NonNull ArrayList availableFonts) { + final String familyName = xmlFamily.getName(); + final FontFamily family = createFontFamily( + familyName, Arrays.asList(xmlFamily.getFonts()), + xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, availableFonts); + if (family == null) { + return; + } + final ArrayList fallback = new ArrayList<>(); + fallback.add(family); + fallbackListMap.put(familyName, fallback); + } + /** * Build the system fallback from xml file. * @@ -226,11 +239,12 @@ public final class SystemFonts { @VisibleForTesting public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, @NonNull String fontDir, + @NonNull FontCustomizationParser.Result oemCustomization, @NonNull ArrayMap fallbackMap, @NonNull ArrayList availableFonts) { try { final FileInputStream fontsIn = new FileInputStream(xmlPath); - final FontConfig fontConfig = FontListParser.parse(fontsIn); + final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir); final HashMap bufferCache = new HashMap(); final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); @@ -242,16 +256,12 @@ public final class SystemFonts { if (familyName == null) { continue; } - final FontFamily family = createFontFamily( - xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()), - xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir, - availableFonts); - if (family == null) { - continue; - } - final ArrayList fallback = new ArrayList<>(); - fallback.add(family); - fallbackListMap.put(familyName, fallback); + appendNamedFamily(xmlFamily, bufferCache, fallbackListMap, availableFonts); + } + + for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) { + appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i), + bufferCache, fallbackListMap, availableFonts); } // Then, add fallback fonts to the each fallback map. @@ -260,8 +270,7 @@ public final class SystemFonts { // The first family (usually the sans-serif family) is always placed immediately // after the primary family in the fallback. if (i == 0 || xmlFamily.getName() == null) { - pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir, - availableFonts); + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, availableFonts); } } @@ -274,20 +283,36 @@ public final class SystemFonts { fallbackMap.put(fallbackName, families); } - return fontConfig.getAliases(); + final ArrayList list = new ArrayList<>(); + list.addAll(Arrays.asList(fontConfig.getAliases())); + list.addAll(oemCustomization.mAdditionalAliases); + return list.toArray(new FontConfig.Alias[list.size()]); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Failed initialize system fallbacks.", e); return ArrayUtils.emptyArray(FontConfig.Alias.class); } } + private static FontCustomizationParser.Result readFontCustomization( + @NonNull String customizeXml, @NonNull String customFontsDir) { + try (FileInputStream f = new FileInputStream(customizeXml)) { + return FontCustomizationParser.parse(f, customFontsDir); + } catch (IOException e) { + return new FontCustomizationParser.Result(); + } catch (XmlPullParserException e) { + Log.e(TAG, "Failed to parse font customization XML", e); + return new FontCustomizationParser.Result(); + } + } + static { final ArrayMap systemFallbackMap = new ArrayMap<>(); final ArrayList availableFonts = new ArrayList<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - systemFallbackMap, availableFonts); + oemCustomization, systemFallbackMap, availableFonts); sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap); sAvailableFonts = Collections.unmodifiableList(availableFonts); } - } diff --git a/tools/fonts/add_additional_fonts.py b/tools/fonts/add_additional_fonts.py deleted file mode 100644 index bf4af2b1c56ee..0000000000000 --- a/tools/fonts/add_additional_fonts.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import sys - -def main(argv): - original_file = 'frameworks/base/data/fonts/fonts.xml' - - if len(argv) == 3: - output_file_path = argv[1] - override_file_path = argv[2] - else: - raise ValueError("Wrong number of arguments %s" % len(argv)) - - fallbackPlaceholderFound = False - with open(original_file, 'r') as input_file: - with open(output_file_path, 'w') as output_file: - for line in input_file: - # If we've found the spot to add additional fonts, add them. - if line.strip() == '': - fallbackPlaceholderFound = True - with open(override_file_path) as override_file: - for override_line in override_file: - output_file.write(override_line) - output_file.write(line) - if not fallbackPlaceholderFound: - raise ValueError(' not found in source file: %s' % original_file) - -if __name__ == '__main__': - main(sys.argv)