Loading core/java/android/text/FontConfig.java +147 −2 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontFamily.Builder.VariableFontFamilyType; import android.graphics.fonts.FontFamily.Builder.VariableFontFamilyType; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.FontVariationAxis; import android.icu.util.ULocale; import android.os.Build; import android.os.Build; import android.os.LocaleList; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcel; Loading @@ -39,6 +40,7 @@ import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.List; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Objects; Loading @@ -58,6 +60,7 @@ public final class FontConfig implements Parcelable { private final @NonNull List<FontFamily> mFamilies; private final @NonNull List<FontFamily> mFamilies; private final @NonNull List<Alias> mAliases; private final @NonNull List<Alias> mAliases; private final @NonNull List<NamedFamilyList> mNamedFamilyLists; private final @NonNull List<NamedFamilyList> mNamedFamilyLists; private final @NonNull List<Customization.LocaleFallback> mLocaleFallbackCustomizations; private final long mLastModifiedTimeMillis; private final long mLastModifiedTimeMillis; private final int mConfigVersion; private final int mConfigVersion; Loading @@ -71,10 +74,12 @@ public final class FontConfig implements Parcelable { */ */ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, @NonNull List<NamedFamilyList> namedFamilyLists, @NonNull List<NamedFamilyList> namedFamilyLists, @NonNull List<Customization.LocaleFallback> localeFallbackCustomizations, long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { mFamilies = families; mFamilies = families; mAliases = aliases; mAliases = aliases; mNamedFamilyLists = namedFamilyLists; mNamedFamilyLists = namedFamilyLists; mLocaleFallbackCustomizations = localeFallbackCustomizations; mLastModifiedTimeMillis = lastModifiedTimeMillis; mLastModifiedTimeMillis = lastModifiedTimeMillis; mConfigVersion = configVersion; mConfigVersion = configVersion; } } Loading @@ -84,7 +89,8 @@ public final class FontConfig implements Parcelable { */ */ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { this(families, aliases, Collections.emptyList(), lastModifiedTimeMillis, configVersion); this(families, aliases, Collections.emptyList(), Collections.emptyList(), lastModifiedTimeMillis, configVersion); } } Loading Loading @@ -112,6 +118,18 @@ public final class FontConfig implements Parcelable { return mNamedFamilyLists; return mNamedFamilyLists; } } /** * Returns a locale fallback customizations. * * This field is used for creating the system fallback in the system server. This field is * always empty in the application process. * * @hide */ public @NonNull List<Customization.LocaleFallback> getLocaleFallbackCustomizations() { return mLocaleFallbackCustomizations; } /** /** * Returns the last modified time in milliseconds. * Returns the last modified time in milliseconds. * * Loading Loading @@ -169,7 +187,9 @@ public final class FontConfig implements Parcelable { source.readTypedList(familyLists, NamedFamilyList.CREATOR); source.readTypedList(familyLists, NamedFamilyList.CREATOR); long lastModifiedDate = source.readLong(); long lastModifiedDate = source.readLong(); int configVersion = source.readInt(); int configVersion = source.readInt(); return new FontConfig(families, aliases, familyLists, lastModifiedDate, configVersion); return new FontConfig(families, aliases, familyLists, Collections.emptyList(), // Don't need to pass customization to API caller. lastModifiedDate, configVersion); } } @Override @Override Loading Loading @@ -813,4 +833,129 @@ public final class FontConfig implements Parcelable { + '}'; + '}'; } } } } /** @hide */ public static class Customization { private Customization() {} // Singleton /** * A class that represents customization of locale fallback * * This class represents a vendor customization of new-locale-family. * * <pre> * <family customizationType="new-locale-family" operation="prepend" lang="ja-JP"> * <font weight="400" style="normal">MyAlternativeFont.ttf * <axis tag="wght" stylevalue="400"/> * </font> * </family> * </pre> * * The operation can be one of prepend, replace or append. The operation prepend means that * the new font family is inserted just before the original font family. The original font * family is still in the fallback. The operation replace means that the original font * family is replaced with new font family. The original font family is removed from the * fallback. The operation append means that the new font family is inserted just after the * original font family. The original font family is still in the fallback. * * The lang attribute is a BCP47 compliant language tag. The font fallback mainly uses ISO * 15924 script code for matching. If the script code is missing, most likely script code * will be used. */ public static class LocaleFallback { private final Locale mLocale; private final int mOperation; private final FontFamily mFamily; private final String mScript; public static final int OPERATION_PREPEND = 0; public static final int OPERATION_APPEND = 1; public static final int OPERATION_REPLACE = 2; /** @hide */ @Retention(SOURCE) @IntDef(prefix = { "OPERATION_" }, value = { OPERATION_PREPEND, OPERATION_APPEND, OPERATION_REPLACE }) public @interface Operation {} public LocaleFallback(@NonNull Locale locale, @Operation int operation, @NonNull FontFamily family) { mLocale = locale; mOperation = operation; mFamily = family; mScript = resolveScript(locale); } /** * A customization target locale. * @return a locale */ public @NonNull Locale getLocale() { return mLocale; } /** * An operation to be applied to the original font family. * * The operation can be one of {@link #OPERATION_PREPEND}, {@link #OPERATION_REPLACE} or * {@link #OPERATION_APPEND}. * * The operation prepend ({@link #OPERATION_PREPEND}) means that the new font family is * inserted just before the original font family. The original font family is still in * the fallback. * * The operation replace ({@link #OPERATION_REPLACE}) means that the original font * family is replaced with new font family. The original font family is removed from the * fallback. * * The operation append ({@link #OPERATION_APPEND}) means that the new font family is * inserted just after the original font family. The original font family is still in * the fallback. * * @return an operation. */ public @Operation int getOperation() { return mOperation; } /** * Returns a family to be inserted or replaced to the fallback. * * @return a family */ public @NonNull FontFamily getFamily() { return mFamily; } /** * Returns a script of the locale. If the script is missing in the given locale, the * most likely locale is returned. */ public @NonNull String getScript() { return mScript; } @Override public String toString() { return "LocaleFallback{" + "mLocale=" + mLocale + ", mOperation=" + mOperation + ", mFamily=" + mFamily + '}'; } } } /** @hide */ public static String resolveScript(Locale locale) { String script = locale.getScript(); if (script != null && !script.isEmpty()) { return script; } return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript(); } } } core/java/android/text/flags/custom_locale_fallback.aconfig 0 → 100644 +8 −0 Original line number Original line Diff line number Diff line package: "com.android.text.flags" flag { name: "custom_locale_fallback" namespace: "text" description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font." bug: "" } core/tests/coretests/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -64,6 +64,7 @@ android_test { "servicestests-utils", "servicestests-utils", "device-time-shell-utils", "device-time-shell-utils", "testables", "testables", "com.android.text.flags-aconfig-java", ], ], libs: [ libs: [ Loading core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +134 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.graphics; package android.graphics; import static com.android.text.flags.Flags.FLAG_CUSTOM_LOCALE_FALLBACK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull; Loading @@ -30,6 +32,9 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.graphics.fonts.SystemFonts; import android.graphics.text.PositionedGlyphs; import android.graphics.text.PositionedGlyphs; import android.graphics.text.TextRunShaper; import android.graphics.text.TextRunShaper; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.text.FontConfig; import android.text.FontConfig; import android.util.ArrayMap; import android.util.ArrayMap; Loading @@ -39,6 +44,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -107,6 +113,10 @@ public class TypefaceSystemFallbackTest { GLYPH_2EM_WIDTH = paint.measureText("a"); GLYPH_2EM_WIDTH = paint.measureText("a"); } } @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before @Before public void setUp() { public void setUp() { final AssetManager am = final AssetManager am = Loading Loading @@ -877,6 +887,130 @@ public class TypefaceSystemFallbackTest { assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); } } private static void assertA3emFontIsUsed(Typeface typeface) { final Paint paint = new Paint(); assertNotNull(typeface); paint.setTypeface(typeface); assertTrue("a3em font must be used", GLYPH_3EM_WIDTH == paint.measureText("a") && GLYPH_1EM_WIDTH == paint.measureText("b") && GLYPH_1EM_WIDTH == paint.measureText("c")); } private static void assertB3emFontIsUsed(Typeface typeface) { final Paint paint = new Paint(); assertNotNull(typeface); paint.setTypeface(typeface); assertTrue("b3em font must be used", GLYPH_1EM_WIDTH == paint.measureText("a") && GLYPH_3EM_WIDTH == paint.measureText("b") && GLYPH_1EM_WIDTH == paint.measureText("c")); } private static String getBaseXml(String font, String lang) { final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + "<familyset>" + " <family>" + " <font weight='400' style='normal'>no_coverage.ttf</font>" + " </family>" + " <family name='named-family'>" + " <font weight='400' style='normal'>no_coverage.ttf</font>" + " </family>" + " <family lang='%s'>" + " <font weight='400' style='normal'>%s</font>" + " </family>" + "</familyset>"; return String.format(xml, lang, font); } private static String getCustomizationXml(String font, String op, String lang) { final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + "<fonts-modification version='1'>" + " <family customizationType='new-locale-family' operation='%s' lang='%s'>" + " <font weight='400' style='normal' fallbackFor='named-family'>%s</font>" + " </family>" + "</fonts-modification>"; return String.format(xml, op, lang, font); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_prepend() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "prepend", "ja-JP"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // operation "prepend" places font before the original font, thus b3em is used. assertB3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_replace() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "replace", "ja-JP"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // operation "replace" removes the original font, thus b3em font is used. assertB3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_append() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "append", "ja-JP"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // operation "append" comes next to the original font, so the original "a3em" is used. assertA3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_ScriptMismatch() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "replace", "ko-KR"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // Since the script doesn't match, the customization is ignored. assertA3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_SubscriptMatch() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "replace", "ko-Hani-KR"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // Hani script is supported by Japanese, Jpan. assertB3emFontIsUsed(typeface); } @Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class) public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() { public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() { final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" Loading @@ -902,7 +1036,6 @@ public class TypefaceSystemFallbackTest { readFontCustomization(oemXml); readFontCustomization(oemXml); } } @Test @Test public void testBuildSystemFallback_UpdatableFont() { public void testBuildSystemFallback_UpdatableFont() { final String xml = "<?xml version='1.0' encoding='UTF-8'?>" final String xml = "<?xml version='1.0' encoding='UTF-8'?>" Loading graphics/java/android/graphics/FontListParser.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -236,7 +236,9 @@ public class FontListParser { } } } } return new FontConfig(families, filtered, resultNamedFamilies, lastModifiedDate, return new FontConfig(families, filtered, resultNamedFamilies, customization.getLocaleFamilyCustomizations(), lastModifiedDate, configVersion); configVersion); } } Loading Loading
core/java/android/text/FontConfig.java +147 −2 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontFamily.Builder.VariableFontFamilyType; import android.graphics.fonts.FontFamily.Builder.VariableFontFamilyType; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.FontVariationAxis; import android.icu.util.ULocale; import android.os.Build; import android.os.Build; import android.os.LocaleList; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcel; Loading @@ -39,6 +40,7 @@ import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.List; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Objects; Loading @@ -58,6 +60,7 @@ public final class FontConfig implements Parcelable { private final @NonNull List<FontFamily> mFamilies; private final @NonNull List<FontFamily> mFamilies; private final @NonNull List<Alias> mAliases; private final @NonNull List<Alias> mAliases; private final @NonNull List<NamedFamilyList> mNamedFamilyLists; private final @NonNull List<NamedFamilyList> mNamedFamilyLists; private final @NonNull List<Customization.LocaleFallback> mLocaleFallbackCustomizations; private final long mLastModifiedTimeMillis; private final long mLastModifiedTimeMillis; private final int mConfigVersion; private final int mConfigVersion; Loading @@ -71,10 +74,12 @@ public final class FontConfig implements Parcelable { */ */ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, @NonNull List<NamedFamilyList> namedFamilyLists, @NonNull List<NamedFamilyList> namedFamilyLists, @NonNull List<Customization.LocaleFallback> localeFallbackCustomizations, long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { mFamilies = families; mFamilies = families; mAliases = aliases; mAliases = aliases; mNamedFamilyLists = namedFamilyLists; mNamedFamilyLists = namedFamilyLists; mLocaleFallbackCustomizations = localeFallbackCustomizations; mLastModifiedTimeMillis = lastModifiedTimeMillis; mLastModifiedTimeMillis = lastModifiedTimeMillis; mConfigVersion = configVersion; mConfigVersion = configVersion; } } Loading @@ -84,7 +89,8 @@ public final class FontConfig implements Parcelable { */ */ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { this(families, aliases, Collections.emptyList(), lastModifiedTimeMillis, configVersion); this(families, aliases, Collections.emptyList(), Collections.emptyList(), lastModifiedTimeMillis, configVersion); } } Loading Loading @@ -112,6 +118,18 @@ public final class FontConfig implements Parcelable { return mNamedFamilyLists; return mNamedFamilyLists; } } /** * Returns a locale fallback customizations. * * This field is used for creating the system fallback in the system server. This field is * always empty in the application process. * * @hide */ public @NonNull List<Customization.LocaleFallback> getLocaleFallbackCustomizations() { return mLocaleFallbackCustomizations; } /** /** * Returns the last modified time in milliseconds. * Returns the last modified time in milliseconds. * * Loading Loading @@ -169,7 +187,9 @@ public final class FontConfig implements Parcelable { source.readTypedList(familyLists, NamedFamilyList.CREATOR); source.readTypedList(familyLists, NamedFamilyList.CREATOR); long lastModifiedDate = source.readLong(); long lastModifiedDate = source.readLong(); int configVersion = source.readInt(); int configVersion = source.readInt(); return new FontConfig(families, aliases, familyLists, lastModifiedDate, configVersion); return new FontConfig(families, aliases, familyLists, Collections.emptyList(), // Don't need to pass customization to API caller. lastModifiedDate, configVersion); } } @Override @Override Loading Loading @@ -813,4 +833,129 @@ public final class FontConfig implements Parcelable { + '}'; + '}'; } } } } /** @hide */ public static class Customization { private Customization() {} // Singleton /** * A class that represents customization of locale fallback * * This class represents a vendor customization of new-locale-family. * * <pre> * <family customizationType="new-locale-family" operation="prepend" lang="ja-JP"> * <font weight="400" style="normal">MyAlternativeFont.ttf * <axis tag="wght" stylevalue="400"/> * </font> * </family> * </pre> * * The operation can be one of prepend, replace or append. The operation prepend means that * the new font family is inserted just before the original font family. The original font * family is still in the fallback. The operation replace means that the original font * family is replaced with new font family. The original font family is removed from the * fallback. The operation append means that the new font family is inserted just after the * original font family. The original font family is still in the fallback. * * The lang attribute is a BCP47 compliant language tag. The font fallback mainly uses ISO * 15924 script code for matching. If the script code is missing, most likely script code * will be used. */ public static class LocaleFallback { private final Locale mLocale; private final int mOperation; private final FontFamily mFamily; private final String mScript; public static final int OPERATION_PREPEND = 0; public static final int OPERATION_APPEND = 1; public static final int OPERATION_REPLACE = 2; /** @hide */ @Retention(SOURCE) @IntDef(prefix = { "OPERATION_" }, value = { OPERATION_PREPEND, OPERATION_APPEND, OPERATION_REPLACE }) public @interface Operation {} public LocaleFallback(@NonNull Locale locale, @Operation int operation, @NonNull FontFamily family) { mLocale = locale; mOperation = operation; mFamily = family; mScript = resolveScript(locale); } /** * A customization target locale. * @return a locale */ public @NonNull Locale getLocale() { return mLocale; } /** * An operation to be applied to the original font family. * * The operation can be one of {@link #OPERATION_PREPEND}, {@link #OPERATION_REPLACE} or * {@link #OPERATION_APPEND}. * * The operation prepend ({@link #OPERATION_PREPEND}) means that the new font family is * inserted just before the original font family. The original font family is still in * the fallback. * * The operation replace ({@link #OPERATION_REPLACE}) means that the original font * family is replaced with new font family. The original font family is removed from the * fallback. * * The operation append ({@link #OPERATION_APPEND}) means that the new font family is * inserted just after the original font family. The original font family is still in * the fallback. * * @return an operation. */ public @Operation int getOperation() { return mOperation; } /** * Returns a family to be inserted or replaced to the fallback. * * @return a family */ public @NonNull FontFamily getFamily() { return mFamily; } /** * Returns a script of the locale. If the script is missing in the given locale, the * most likely locale is returned. */ public @NonNull String getScript() { return mScript; } @Override public String toString() { return "LocaleFallback{" + "mLocale=" + mLocale + ", mOperation=" + mOperation + ", mFamily=" + mFamily + '}'; } } } /** @hide */ public static String resolveScript(Locale locale) { String script = locale.getScript(); if (script != null && !script.isEmpty()) { return script; } return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript(); } } }
core/java/android/text/flags/custom_locale_fallback.aconfig 0 → 100644 +8 −0 Original line number Original line Diff line number Diff line package: "com.android.text.flags" flag { name: "custom_locale_fallback" namespace: "text" description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font." bug: "" }
core/tests/coretests/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -64,6 +64,7 @@ android_test { "servicestests-utils", "servicestests-utils", "device-time-shell-utils", "device-time-shell-utils", "testables", "testables", "com.android.text.flags-aconfig-java", ], ], libs: [ libs: [ Loading
core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +134 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.graphics; package android.graphics; import static com.android.text.flags.Flags.FLAG_CUSTOM_LOCALE_FALLBACK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull; Loading @@ -30,6 +32,9 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.graphics.fonts.SystemFonts; import android.graphics.text.PositionedGlyphs; import android.graphics.text.PositionedGlyphs; import android.graphics.text.TextRunShaper; import android.graphics.text.TextRunShaper; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.text.FontConfig; import android.text.FontConfig; import android.util.ArrayMap; import android.util.ArrayMap; Loading @@ -39,6 +44,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -107,6 +113,10 @@ public class TypefaceSystemFallbackTest { GLYPH_2EM_WIDTH = paint.measureText("a"); GLYPH_2EM_WIDTH = paint.measureText("a"); } } @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before @Before public void setUp() { public void setUp() { final AssetManager am = final AssetManager am = Loading Loading @@ -877,6 +887,130 @@ public class TypefaceSystemFallbackTest { assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); } } private static void assertA3emFontIsUsed(Typeface typeface) { final Paint paint = new Paint(); assertNotNull(typeface); paint.setTypeface(typeface); assertTrue("a3em font must be used", GLYPH_3EM_WIDTH == paint.measureText("a") && GLYPH_1EM_WIDTH == paint.measureText("b") && GLYPH_1EM_WIDTH == paint.measureText("c")); } private static void assertB3emFontIsUsed(Typeface typeface) { final Paint paint = new Paint(); assertNotNull(typeface); paint.setTypeface(typeface); assertTrue("b3em font must be used", GLYPH_1EM_WIDTH == paint.measureText("a") && GLYPH_3EM_WIDTH == paint.measureText("b") && GLYPH_1EM_WIDTH == paint.measureText("c")); } private static String getBaseXml(String font, String lang) { final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + "<familyset>" + " <family>" + " <font weight='400' style='normal'>no_coverage.ttf</font>" + " </family>" + " <family name='named-family'>" + " <font weight='400' style='normal'>no_coverage.ttf</font>" + " </family>" + " <family lang='%s'>" + " <font weight='400' style='normal'>%s</font>" + " </family>" + "</familyset>"; return String.format(xml, lang, font); } private static String getCustomizationXml(String font, String op, String lang) { final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + "<fonts-modification version='1'>" + " <family customizationType='new-locale-family' operation='%s' lang='%s'>" + " <font weight='400' style='normal' fallbackFor='named-family'>%s</font>" + " </family>" + "</fonts-modification>"; return String.format(xml, op, lang, font); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_prepend() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "prepend", "ja-JP"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // operation "prepend" places font before the original font, thus b3em is used. assertB3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_replace() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "replace", "ja-JP"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // operation "replace" removes the original font, thus b3em font is used. assertB3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_append() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "append", "ja-JP"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // operation "append" comes next to the original font, so the original "a3em" is used. assertA3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_ScriptMismatch() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "replace", "ko-KR"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // Since the script doesn't match, the customization is ignored. assertA3emFontIsUsed(typeface); } @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) @Test public void testBuildSystemFallback__Customization_locale_SubscriptMatch() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); buildSystemFallback( getBaseXml("a3em.ttf", "ja-JP"), getCustomizationXml("b3em.ttf", "replace", "ko-Hani-KR"), fontMap, fallbackMap); Typeface typeface = fontMap.get("named-family"); // Hani script is supported by Japanese, Jpan. assertB3emFontIsUsed(typeface); } @Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class) public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() { public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() { final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" Loading @@ -902,7 +1036,6 @@ public class TypefaceSystemFallbackTest { readFontCustomization(oemXml); readFontCustomization(oemXml); } } @Test @Test public void testBuildSystemFallback_UpdatableFont() { public void testBuildSystemFallback_UpdatableFont() { final String xml = "<?xml version='1.0' encoding='UTF-8'?>" final String xml = "<?xml version='1.0' encoding='UTF-8'?>" Loading
graphics/java/android/graphics/FontListParser.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -236,7 +236,9 @@ public class FontListParser { } } } } return new FontConfig(families, filtered, resultNamedFamilies, lastModifiedDate, return new FontConfig(families, filtered, resultNamedFamilies, customization.getLocaleFamilyCustomizations(), lastModifiedDate, configVersion); configVersion); } } Loading