diff options
4 files changed, 106 insertions, 31 deletions
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 82d066f6ab16..05ff21853131 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -67,6 +67,7 @@ public class TypefaceSystemFallbackTest { private static final String TEST_FONT_DIR; private static final String TEST_OEM_XML; private static final String TEST_OEM_DIR; + private static final String TEST_UPDATABLE_FONT_DIR; private static final float GLYPH_1EM_WIDTH; private static final float GLYPH_2EM_WIDTH; @@ -82,9 +83,11 @@ public class TypefaceSystemFallbackTest { 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(); + TEST_UPDATABLE_FONT_DIR = cacheDir.getAbsolutePath() + "/updatable_fonts/"; new File(TEST_FONT_DIR).mkdirs(); new File(TEST_OEM_DIR).mkdirs(); + new File(TEST_UPDATABLE_FONT_DIR).mkdirs(); final AssetManager am = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); @@ -103,18 +106,11 @@ public class TypefaceSystemFallbackTest { InstrumentationRegistry.getInstrumentation().getContext().getAssets(); for (final String fontFile : TEST_FONT_FILES) { final String sourceInAsset = "fonts/" + fontFile; - final File outInCache = new File(TEST_FONT_DIR, fontFile); - try (InputStream is = am.open(sourceInAsset)) { - Files.copy(is, outInCache.toPath(), StandardCopyOption.REPLACE_EXISTING); - } 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); - } + copyAssetToFile(sourceInAsset, new File(TEST_FONT_DIR, fontFile)); + copyAssetToFile(sourceInAsset, new File(TEST_OEM_DIR, fontFile)); + } + for (final File fontFile : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) { + fontFile.delete(); } } @@ -124,7 +120,20 @@ public class TypefaceSystemFallbackTest { final File outInCache = new File(TEST_FONT_DIR, fontFile); outInCache.delete(); final File outOemInCache = new File(TEST_OEM_DIR, fontFile); - outInCache.delete(); + outOemInCache.delete(); + } + for (final File fontFile : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) { + fontFile.delete(); + } + } + + private static void copyAssetToFile(String sourceInAsset, File out) { + final AssetManager am = + InstrumentationRegistry.getInstrumentation().getContext().getAssets(); + try (InputStream is = am.open(sourceInAsset)) { + Files.copy(is, out.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); } } @@ -138,7 +147,7 @@ public class TypefaceSystemFallbackTest { } final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, oemCustomization, fallbackMap); + TEST_FONT_DIR, TEST_UPDATABLE_FONT_DIR, oemCustomization, fallbackMap); Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); } @@ -835,4 +844,32 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; readFontCustomization(oemXml); } + + + @Test + public void testBuildSystemFallback_UpdatableFont() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='test'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); + + // Install all2em.ttf as a3em.ttf + copyAssetToFile("fonts/all2em.ttf", new File(TEST_UPDATABLE_FONT_DIR, "a3em.ttf")); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface sansSerifTypeface = fontMap.get("test"); + assertNotNull(sansSerifTypeface); + paint.setTypeface(sansSerifTypeface); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("c"), 0.0f); + } } diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 0782f8dfd9d3..73fff7207c45 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -16,6 +16,7 @@ package android.graphics; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontVariationAxis; import android.os.Build; @@ -25,6 +26,7 @@ import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -41,26 +43,26 @@ public class FontListParser { /* Parse fallback list (no names) */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { - return parse(in, "/system/fonts"); + return parse(in, "/system/fonts", null); } /** * Parse the fonts.xml */ - public static FontConfig parse(InputStream in, String fontDir) - throws XmlPullParserException, IOException { + public static FontConfig parse(InputStream in, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser, fontDir); + return readFamilies(parser, fontDir, updatableFontDir); } finally { in.close(); } } - private static FontConfig readFamilies(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { + private static FontConfig readFamilies(XmlPullParser parser, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { List<FontConfig.Family> families = new ArrayList<>(); List<FontConfig.Alias> aliases = new ArrayList<>(); @@ -69,7 +71,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - families.add(readFamily(parser, fontDir)); + families.add(readFamily(parser, fontDir, updatableFontDir)); } else if (tag.equals("alias")) { aliases.add(readAlias(parser)); } else { @@ -83,8 +85,8 @@ public class FontListParser { /** * Reads a family element */ - public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { + public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { final String name = parser.getAttributeValue(null, "name"); final String lang = parser.getAttributeValue("", "lang"); final String variant = parser.getAttributeValue(null, "variant"); @@ -93,7 +95,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; final String tag = parser.getName(); if (tag.equals("font")) { - fonts.add(readFont(parser, fontDir)); + fonts.add(readFont(parser, fontDir, updatableFontDir)); } else { skip(parser); } @@ -114,8 +116,8 @@ 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, String fontDir) - throws XmlPullParserException, IOException { + private static FontConfig.Font readFont(XmlPullParser parser, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); @@ -137,10 +139,22 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - return new FontConfig.Font(fontDir + sanitizedName, index, axes.toArray( + String fontName = findFontFile(sanitizedName, fontDir, updatableFontDir); + return new FontConfig.Font(fontName, index, axes.toArray( new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); } + private static String findFontFile(String fileName, String fontDir, + @Nullable String updatableFontDir) { + if (updatableFontDir != null) { + String updatableFontName = updatableFontDir + fileName; + if (new File(updatableFontName).exists()) { + return updatableFontName; + } + } + return fontDir + fileName; + } + private static FontVariationAxis readAxis(XmlPullParser parser) throws XmlPullParserException, IOException { String tagStr = parser.getAttributeValue(null, "tag"); diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index 0291d7484dc5..f95da82ee07c 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -97,7 +97,7 @@ public class FontCustomizationParser { throw new IllegalArgumentException("customizationType must be specified"); } if (customizationType.equals("new-named-family")) { - out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir)); + out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir, null)); } 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 fb6ea99be7ab..16a53c25db08 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -91,7 +91,9 @@ public final class SystemFonts { final FontCustomizationParser.Result oemCustomization = readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); Map<String, FontFamily[]> map = new ArrayMap<>(); - buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", oemCustomization, map); + // TODO: use updated fonts + buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", null /* updatableFontDir */, + oemCustomization, map); Set<Font> res = new HashSet<>(); for (FontFamily[] families : map.values()) { for (FontFamily family : families) { @@ -226,11 +228,26 @@ public final class SystemFonts { } /** + * @see #buildSystemFallback(String, String, String, FontCustomizationParser.Result, Map) + * @hide + */ + @VisibleForTesting + public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, + @NonNull String fontDir, + @NonNull FontCustomizationParser.Result oemCustomization, + @NonNull Map<String, FontFamily[]> fallbackMap) { + return buildSystemFallback(xmlPath, fontDir, null /* updatableFontDir */, + oemCustomization, fallbackMap); + } + + /** * Build the system fallback from xml file. * * @param xmlPath A full path string to the fonts.xml file. * @param fontDir A full path string to the system font directory. This must end with * slash('/'). + * @param updatableFontDir A full path string to the updatable system font directory. This + * must end with slash('/'). * @param fallbackMap An output system fallback map. Caller must pass empty map. * @return a list of aliases * @hide @@ -238,11 +255,12 @@ public final class SystemFonts { @VisibleForTesting public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, @NonNull String fontDir, + @Nullable String updatableFontDir, @NonNull FontCustomizationParser.Result oemCustomization, @NonNull Map<String, FontFamily[]> fallbackMap) { try { final FileInputStream fontsIn = new FileInputStream(xmlPath); - final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir); + final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir, updatableFontDir); final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>(); final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); @@ -306,11 +324,17 @@ public final class SystemFonts { /** @hide */ public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>> initializePreinstalledFonts() { + return initializeSystemFonts(null); + } + + /** @hide */ + public static Pair<FontConfig.Alias[], Map<String, FontFamily[]>> + initializeSystemFonts(@Nullable String updatableFontDir) { final FontCustomizationParser.Result oemCustomization = readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); Map<String, FontFamily[]> map = new ArrayMap<>(); FontConfig.Alias[] aliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - oemCustomization, map); + updatableFontDir, oemCustomization, map); synchronized (LOCK) { sFamilyMap = map; } |