Introduce OEM customization XML parser

As the initial version of the OEM customization XML, support
new-named-family customization. This allows OEMs to add new named
family.

Bug: 111544833
Test: atest FrameworksCoreTests:android.graphics
Change-Id: If58711fc038898175fcad0ae095865312bd738e2
This commit is contained in:
Seigo Nonaka
2018-09-26 22:11:31 -07:00
parent 914857349e
commit 3328d1ce55
8 changed files with 440 additions and 108 deletions

View File

@@ -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<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap,
ArrayMap<String, FontFamily[]> fallbackMap) {
final ArrayList<Font> 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<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
final ArrayList<Font> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 {
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ "</familyset>";
final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<fonts-modification version='1'>"
+ " <family customizationType='new-named-family' name='google-sans'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</fonts-modification>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ "</familyset>";
final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<fonts-modification version='1'>"
+ " <family customizationType='new-named-family' name='sans-serif'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</fonts-modification>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ "</familyset>";
final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<fonts-modification version='1'>"
+ " <family customizationType='new-named-family' name='google-sans'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " <font weight='700' style='normal'>c3em.ttf</font>"
+ " </family>"
+ " <alias name='another-google-sans' to='google-sans' />"
+ " <alias name='google-sans-bold' to='google-sans' weight='700' />"
+ "</fonts-modification>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset>"
+ " <family name='named-family'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ " <alias name='named-alias' to='named-family' />"
+ "</familyset>";
final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<fonts-modification version='1'>"
+ " <family customizationType='new-named-family' name='named-alias'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</fonts-modification>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> 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 = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<fonts-modification version='1'>"
+ " <family customizationType='new-named-family'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</fonts-modification>";
readFontCustomization(oemXml);
}
@Test(expected = IllegalArgumentException.class)
public void testBuildSystemFallback__Customization_new_named_family_dup_name_exception() {
final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<fonts-modification version='1'>"
+ " <family customizationType='new-named-family' name='google-sans'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ " <family customizationType='new-named-family' name='google-sans'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</fonts-modification>";
readFontCustomization(oemXml);
}
}

View File

@@ -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<String, FontFamily[]> fallbackMap = new ArrayMap<>();
final ArrayList<Font> 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);
}

View File

@@ -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)

View File

@@ -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<FontConfig.Family> families = new ArrayList<>();
List<FontConfig.Alias> 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()) {

View File

@@ -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 :

View File

@@ -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<FontConfig.Family> mAdditionalNamedFamilies = new ArrayList<>();
ArrayList<FontConfig.Alias> 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<String> 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);
}
}
}

View File

@@ -113,7 +113,6 @@ public final class SystemFonts {
private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
@NonNull Map<String, ByteBuffer> cache,
@NonNull String fontDir,
@NonNull ArrayList<Font> 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<String, ByteBuffer> cache,
@NonNull String fontDir,
@NonNull ArrayList<Font> 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<String, ByteBuffer> bufferCache,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap,
@NonNull ArrayList<Font> 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<FontFamily> 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<String, FontFamily[]> fallbackMap,
@NonNull ArrayList<Font> availableFonts) {
try {
final FileInputStream fontsIn = new FileInputStream(xmlPath);
final FontConfig fontConfig = FontListParser.parse(fontsIn);
final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir);
final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>();
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<FontFamily> 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<FontConfig.Alias> 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<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
final ArrayList<Font> 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);
}
}

View File

@@ -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() == '<!-- fallback fonts -->':
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('<!-- fallback fonts --> not found in source file: %s' % original_file)
if __name__ == '__main__':
main(sys.argv)