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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user