diff options
| author | 2021-01-20 04:18:20 +0000 | |
|---|---|---|
| committer | 2021-01-20 04:18:20 +0000 | |
| commit | 903dc9269f78153202339c9728c50f477bacae2e (patch) | |
| tree | 97bf075ae3a2311651379383d731b8c2292e3b2e | |
| parent | 121f5679a863ccb301a05380e0061454fed3e123 (diff) | |
| parent | f5859819cdbfe5e3e7f3eefbe4530ca3170f542d (diff) | |
Merge "Refactor font parser for making SystemApi."
10 files changed, 715 insertions, 396 deletions
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 1878d61c78ac..8492363eb503 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -19,171 +19,268 @@ package android.text; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; -import android.net.Uri; import android.os.Build; +import android.os.LocaleList; +import java.io.File; import java.lang.annotation.Retention; +import java.util.List; /** * Font configuration descriptions for System fonts. - * @hide + * @hide // TODO Make this SystemApi. */ public final class FontConfig { - private final @NonNull Family[] mFamilies; - private final @NonNull Alias[] mAliases; + private final @NonNull List<Family> mFamilies; + private final @NonNull List<Alias> mAliases; - public FontConfig(@NonNull Family[] families, @NonNull Alias[] aliases) { + /** + * Construct a SystemFontConfig instance. + * + * @param families a list of font families. + * @param aliases a list of aliases. + * + * @hide Only system server can create this instance and passed via IPC. + */ + public FontConfig(@NonNull List<Family> families, @NonNull List<Alias> aliases) { mFamilies = families; mAliases = aliases; } /** * Returns the ordered list of families included in the system fonts. + * + * @return a list of font families. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @NonNull Family[] getFamilies() { + public @NonNull List<Family> getFontFamilies() { return mFamilies; } /** * Returns the list of aliases defined for the font families in the system fonts. + * + * @return a list of font families. */ - public @NonNull Alias[] getAliases() { + public @NonNull List<Alias> getAliases() { return mAliases; } /** - * Class that holds information about a Font. + * Returns the ordered list of families included in the system fonts. + * @deprecated Use getFontFamilies instead. + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @NonNull Family[] getFamilies() { + return mFamilies.toArray(new Family[0]); + } + + /** + * A class represents single font entry in system font configuration. */ public static final class Font { - private final @NonNull String mFontName; - private final int mTtcIndex; - private final @NonNull FontVariationAxis[] mAxes; - private final int mWeight; - private final boolean mIsItalic; - private Uri mUri; - private final String mFallbackFor; + private final @NonNull File mFilePath; + private final @Nullable File mOriginalPath; + private final @NonNull FontStyle mStyle; + private final @IntRange(from = 0) int mIndex; + private final @NonNull String mFontVariationSettings; + private final @Nullable String mFallback; /** - * @hide + * Construct a Font instance. + * + * @hide Only system server can create this instance and passed via IPC. */ - public Font(@NonNull String fontName, int ttcIndex, @NonNull FontVariationAxis[] axes, - int weight, boolean isItalic, String fallbackFor) { - mFontName = fontName; - mTtcIndex = ttcIndex; - mAxes = axes; - mWeight = weight; - mIsItalic = isItalic; - mFallbackFor = fallbackFor; + public Font(@NonNull File filePath, @Nullable File originalPath, @NonNull FontStyle style, + @IntRange(from = 0) int index, @NonNull String fontVariationSettings, + @Nullable String fallback) { + mFilePath = filePath; + mOriginalPath = originalPath; + mStyle = style; + mIndex = index; + mFontVariationSettings = fontVariationSettings; + mFallback = fallback; + } + + /** + * Returns a file to the font file. + * + * @return a font file. + */ + public @NonNull File getFilePath() { + return mFilePath; + } + + /** + * Returns an original font file in the system directory. + * + * If the font file is not updated, returns null. + * + * @return returns the original font file in the system if the font file is updated. Returns + * null if the font file is not updated. + */ + public @Nullable File getOriginalPath() { + return mOriginalPath; + } + + /** + * Returns a font style. + * + * @return a font style. + */ + public @NonNull FontStyle getStyle() { + return mStyle; + } + + /** + * Returns a font index. + * + * @return a font index. + */ + public @IntRange(from = 0) int getIndex() { + return mIndex; } /** - * Returns the name associated by the system to this font. + * Return a font variation settings. + * + * @return a font variation settings. */ - public @NonNull String getFontName() { - return mFontName; + public @NonNull String getFontVariationSettings() { + return mFontVariationSettings; + } + + /** + * Returns font family name that uses this font as a fallback. + * + * If this font is a fallback for the default font family, this is null. + * + * @return a font family name. + */ + public @Nullable String getFallback() { + return mFallback; } /** * Returns the index to be used to access this font when accessing a TTC file. + * @deprecated Use getIndex instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getTtcIndex() { - return mTtcIndex; + return mIndex; } /** * Returns the list of axes associated to this font. + * @deprecated Use getFontVariationSettings + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @NonNull FontVariationAxis[] getAxes() { - return mAxes; + return FontVariationAxis.fromFontVariationSettings(mFontVariationSettings); } /** * Returns the weight value for this font. + * @deprecated Use getStyle instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getWeight() { - return mWeight; + return getStyle().getWeight(); } /** * Returns whether this font is italic. + * @deprecated Use getStyle instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isItalic() { - return mIsItalic; - } - - /** - * Returns the content uri associated to this font. - * - * You can reach to the font contents by calling {@link - * android.content.ContentResolver#openInputStream}. - */ - public @Nullable Uri getUri() { - return mUri; - } - - public void setUri(@NonNull Uri uri) { - mUri = uri; - } - - public String getFallbackFor() { - return mFallbackFor; + return getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC; } } /** - * Class that holds information about a Font alias. + * A class represents alias between named font families. + * + * In the system font configuration, an font family can be an alias of another font family with + * different font weight. For example, "sans-serif-medium" can be a medium weight of + * sans-serif font family. */ public static final class Alias { - private final @NonNull String mName; - private final @NonNull String mToName; - private final int mWeight; + private final @NonNull String mAliasName; + private final @NonNull String mReferName; + private final @IntRange(from = 0, to = 1000) int mWeight; - public Alias(@NonNull String name, @NonNull String toName, int weight) { - mName = name; - mToName = toName; + /** + * Construct an alias instance. + * + * @param aliasName an alias of the named font family. + * @param referName a referring font family name. + * @param weight a font weight of the referring font family. + * @hide Only system server can create this instance and passed via IPC. + */ + public Alias(@NonNull String aliasName, @NonNull String referName, + @IntRange(from = 0, to = 1000) int weight) { + mAliasName = aliasName; + mReferName = referName; mWeight = weight; } /** - * Returns the new name for the alias. + * An alias of the named font family. + * + * @return an alias of the named font family. */ - public @NonNull String getName() { - return mName; + public @NonNull String getAliasName() { + return mAliasName; } /** - * Returns the existing name to which this alias points to. + * A name of font family referring from {@link #getAliasName()} + * + * @return a referring font family name. */ - public @NonNull String getToName() { - return mToName; + public @NonNull String getReferName() { + return mReferName; } /** - * Returns the weight associated with this alias. + * A font weight of the referring font family. + * + * @return a font weight of the referring font family. */ - public int getWeight() { + public @IntRange(from = 0, to = 1000) int getWeight() { return mWeight; } } /** - * Class that holds information about a Font family. + * A class represents single font family entry in system font configuration. + * + * <p> + * A font family is a bundle of fonts for drawing text in various styles. + * For example, regular style font and bold style font can be bundled into a single font family, + * then system will select the correct style font from family for drawing. */ public static final class Family { - private final @NonNull String mName; - private final @NonNull Font[] mFonts; - // Comma separated BCP47 complient locale strings - private final @NonNull String mLanguages; + private final @NonNull List<Font> mFonts; + private final @Nullable String mName; + private final @Nullable LocaleList mLocaleList; + private final @Variant int mVariant; /** @hide */ @Retention(SOURCE) @@ -212,52 +309,105 @@ public final class FontConfig { /** * Value for font variant. * - * Indiates the font is for elegant variant. + * Indicates the font is for elegant variant. * @see android.graphics.Paint#setElegantTextHeight */ public static final int VARIANT_ELEGANT = 2; - // Must be same with Minikin's variant values. - // See frameworks/minikin/include/minikin/FontFamily.h - private final @Variant int mVariant; - - public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String languages, - @Variant int variant) { - mName = name; + /** + * Construct a family instance. + * + * @hide Only system server can create this instance and passed via IPC. + */ + public Family(@NonNull List<Font> fonts, @Nullable String name, + @Nullable LocaleList localeList, @Variant int variant) { mFonts = fonts; - mLanguages = languages; + mName = name; + mLocaleList = localeList; mVariant = variant; } /** - * Returns the name given by the system to this font family. + * Returns a list of font files in this family. + * + * @return a list of font files. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @Nullable String getName() { + public @NonNull List<Font> getFontList() { + return mFonts; + } + + /** + * Returns a family name if this family defines a new fallback. + * + * @return non-null if a family name is associated. Otherwise null. + */ + public @Nullable String getFallbackName() { return mName; } /** - * Returns the list of fonts included in this family. + * Returns a locale list if associated. + * + * @return non-null if a locale list is associated. Otherwise null. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @Nullable Font[] getFonts() { - return mFonts; + public @NonNull LocaleList getLocaleList() { + return mLocaleList; } /** - * Returns the comma separated BCP47 complient languages for this family. May be null. + * Returns a text height variant. + * + * @return text height variant. */ - public @NonNull String getLanguages() { - return mLanguages; + public @Variant int getTextHeightVariant() { + return mVariant; } /** - * Returns the font variant for this family, e.g. "elegant" or "compact". May be null. + * Returns a family variant associated. + * + * @return a family variant. + * @deprecated Use getTextHeightVariant instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Variant int getVariant() { return mVariant; } + + /** + * Returns a family name if associated. + * + * @return non-null if a family name is associated. Otherwise null. + * @deprecated Use getFallbackName instead. + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable String getName() { + return mName; + } + + /** + * Returns the list of fonts included in this family. + * @deprecated Use getFontFiles instead + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable Font[] getFonts() { + return mFonts.toArray(new Font[0]); + } + + /** + * Returns the comma separated BCP47 compliant languages for this family. May be null. + * @deprecated Use getLocaleList instead + * @hide + */ + @Deprecated + public @NonNull String getLanguages() { + return mLocaleList.toLanguageTags(); + } } } diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 465ea172b1c0..65ea2a8373aa 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.AssetManager; import android.graphics.fonts.FontCustomizationParser; @@ -44,7 +46,6 @@ 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; @@ -139,27 +140,55 @@ public class TypefaceSystemFallbackTest { } } - private static void buildSystemFallback(String xml, - FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap, - ArrayMap<String, FontFamily[]> fallbackMap) { + private static void buildSystemFallback( + @NonNull String xml, + @Nullable String oemXml, + @NonNull ArrayMap<String, Typeface> outFontMap, + @NonNull ArrayMap<String, FontFamily[]> outFallbackMap) { try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { - fos.write(xml.getBytes(Charset.forName("UTF-8"))); + fos.write(xml.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new RuntimeException(e); } + + String oemXmlPath; + if (oemXml != null) { + try (FileOutputStream fos = new FileOutputStream(TEST_OEM_XML)) { + fos.write(oemXml.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + oemXmlPath = TEST_OEM_XML; + } else { + oemXmlPath = null; + } + Map<String, File> updatableFontMap = new HashMap<>(); for (File file : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) { updatableFontMap.put(file.getName(), file); } - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, updatableFontMap, oemCustomization, fallbackMap); - Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse( + TEST_FONTS_XML, TEST_FONT_DIR, oemXmlPath, TEST_OEM_DIR, updatableFontMap); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces( + fontConfig, fallbackMap); + + outFontMap.clear(); + outFontMap.putAll(typefaceMap); + outFallbackMap.clear(); + outFallbackMap.putAll(fallbackMap); } private static FontCustomizationParser.Result readFontCustomization(String oemXml) { try (InputStream is = new ByteArrayInputStream(oemXml.getBytes(StandardCharsets.UTF_8))) { - return FontCustomizationParser.parse(is, TEST_OEM_DIR); + return FontCustomizationParser.parse(is, TEST_OEM_DIR, null); } catch (IOException | XmlPullParserException e) { throw new RuntimeException(e); } @@ -167,19 +196,22 @@ public class TypefaceSystemFallbackTest { @Test public void testBuildSystemFallback() { - final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); - final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, - SYSTEM_FONT_DIR, oemCustomization, fallbackMap); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse( + SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + assertFalse(fontConfig.getAliases().isEmpty()); + assertFalse(fontConfig.getFontFamilies().isEmpty()); - assertNotNull(aliases); + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); assertFalse(fallbackMap.isEmpty()); - Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); - assertFalse(fontMap.isEmpty()); + Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces( + fontConfig, fallbackMap); + assertFalse(typefaceMap.isEmpty()); } @Test @@ -199,10 +231,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); assertEquals(1, fontMap.size()); assertTrue(fontMap.containsKey("sans-serif")); @@ -229,10 +259,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -277,10 +305,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -324,10 +350,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -376,10 +400,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -424,10 +446,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -465,10 +485,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -506,10 +524,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -556,10 +572,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); paint.setTypeface(fontMap.get("sans-serif")); @@ -600,10 +614,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -641,10 +653,8 @@ 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, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -679,10 +689,8 @@ public class TypefaceSystemFallbackTest { + "</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); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -717,10 +725,8 @@ public class TypefaceSystemFallbackTest { + "</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); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -751,10 +757,8 @@ public class TypefaceSystemFallbackTest { + "</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); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -804,10 +808,8 @@ public class TypefaceSystemFallbackTest { + "</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); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -862,12 +864,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(); // Install all2em.ttf as a3em.ttf copyAssetToFile("fonts/all2em.ttf", new File(TEST_UPDATABLE_FONT_DIR, "a3em.ttf")); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java index 392c6b7199a5..d12f495055e1 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java @@ -27,7 +27,6 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import android.text.FontConfig; -import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; @@ -41,7 +40,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.nio.ByteOrder; -import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -197,10 +195,10 @@ public class TypefaceTest { @SmallTest @Test public void testSerialize() throws Exception { - HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> res = - SystemFonts.initializePreinstalledFonts(); - Typeface.initSystemDefaultTypefaces(systemFontMap, res.second, res.first); + FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + Map<String, Typeface> systemFontMap = SystemFonts.buildSystemTypefaces(fontConfig, + fallbackMap); SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); Map<String, Typeface> copiedFontMap = Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN)); diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index 64e6f82d82af..e301037d01f1 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -19,14 +19,15 @@ package android.text; import android.annotation.NonNull; import android.content.Context; import android.content.res.AssetManager; +import android.graphics.FontListParser; import android.graphics.Typeface; -import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; -import android.util.ArrayMap; import androidx.test.InstrumentationRegistry; +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -34,12 +35,13 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.util.Map; public class FontFallbackSetup implements AutoCloseable { private final String[] mTestFontFiles; private final String mXml; private final String mTestFontsDir; - final ArrayMap<String, Typeface> mFontMap = new ArrayMap<>(); + private final Map<String, Typeface> mFontMap; public FontFallbackSetup(@NonNull String testSubDir, @NonNull String[] testFontFiles, @NonNull String xml) { @@ -75,12 +77,15 @@ public class FontFallbackSetup implements AutoCloseable { throw new RuntimeException(e); } - final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, - mTestFontsDir, oemCustomization, fallbackMap); - Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse(testFontsXml, mTestFontsDir, null, null, null); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + mFontMap = SystemFonts.buildSystemTypefaces(fontConfig, fallbackMap); } @NonNull diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index af100c96f6f5..ff381176e38d 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -16,10 +16,14 @@ package android.graphics; +import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.fonts.FontCustomizationParser; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.os.Build; +import android.os.LocaleList; import android.text.FontConfig; import android.util.Xml; @@ -27,6 +31,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -36,7 +41,6 @@ import java.util.regex.Pattern; /** * Parser for font config files. - * * @hide */ public class FontListParser { @@ -44,49 +48,89 @@ 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", null); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser, "/system/fonts/", new FontCustomizationParser.Result(), null); } /** - * Parse the fonts.xml + * Parses system font config XMLs + * + * @param fontsXmlPath location of fonts.xml + * @param systemFontDir location of system font directory + * @param oemCustomizationXmlPath location of oem_customization.xml + * @param productFontDir location of oem customized font directory + * @param updatableFontMap map of updated font files. + * @return font configuration + * @throws IOException + * @throws XmlPullParserException */ - public static FontConfig parse(InputStream in, String fontDir, - @Nullable Map<String, File> updatableFontMap) - throws XmlPullParserException, IOException { - try { + public static FontConfig parse( + @NonNull String fontsXmlPath, + @NonNull String systemFontDir, + @Nullable String oemCustomizationXmlPath, + @Nullable String productFontDir, + @Nullable Map<String, File> updatableFontMap + ) throws IOException, XmlPullParserException { + FontCustomizationParser.Result oemCustomization; + if (oemCustomizationXmlPath != null) { + try (InputStream is = new FileInputStream(oemCustomizationXmlPath)) { + oemCustomization = FontCustomizationParser.parse(is, productFontDir, + updatableFontMap); + } catch (IOException e) { + // OEM customization may not exists. Ignoring + oemCustomization = new FontCustomizationParser.Result(); + } + } else { + oemCustomization = new FontCustomizationParser.Result(); + } + + try (InputStream is = new FileInputStream(fontsXmlPath)) { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + parser.setInput(is, null); parser.nextTag(); - return readFamilies(parser, fontDir, updatableFontMap); - } finally { - in.close(); + return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap); } } - private static FontConfig readFamilies(XmlPullParser parser, String fontDir, + private static FontConfig readFamilies( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @NonNull FontCustomizationParser.Result customization, @Nullable Map<String, File> updatableFontMap) throws XmlPullParserException, IOException { List<FontConfig.Family> families = new ArrayList<>(); - List<FontConfig.Alias> aliases = new ArrayList<>(); + List<FontConfig.Alias> aliases = new ArrayList<>(customization.getAdditionalAliases()); + + Map<String, FontConfig.Family> oemNamedFamilies = + customization.getAdditionalNamedFamilies(); parser.require(XmlPullParser.START_TAG, null, "familyset"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - families.add(readFamily(parser, fontDir, updatableFontMap)); + FontConfig.Family family = readFamily(parser, fontDir, updatableFontMap); + String name = family.getFallbackName(); + if (name == null || !oemNamedFamilies.containsKey(name)) { + // The OEM customization overrides system named family. Skip if OEM + // customization XML defines the same named family. + families.add(family); + } } else if (tag.equals("alias")) { aliases.add(readAlias(parser)); } else { skip(parser); } } - return new FontConfig(families.toArray(new FontConfig.Family[families.size()]), - aliases.toArray(new FontConfig.Alias[aliases.size()])); + + families.addAll(oemNamedFamilies.values()); + return new FontConfig(families, aliases); } /** - * Reads a family element + * Read family tag in fonts.xml or oem_customization.xml */ public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir, @Nullable Map<String, File> updatableFontMap) @@ -94,7 +138,7 @@ public class FontListParser { final String name = parser.getAttributeValue(null, "name"); final String lang = parser.getAttributeValue("", "lang"); final String variant = parser.getAttributeValue(null, "variant"); - final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>(); + final List<FontConfig.Font> fonts = new ArrayList<>(); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; final String tag = parser.getName(); @@ -112,20 +156,22 @@ public class FontListParser { intVariant = FontConfig.Family.VARIANT_ELEGANT; } } - return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang, - intVariant); + return new FontConfig.Family(fonts, name, LocaleList.forLanguageTags(lang), intVariant); } /** Matches leading and trailing XML whitespace. */ private static final Pattern FILENAME_WHITESPACE_PATTERN = Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); - private static FontConfig.Font readFont(XmlPullParser parser, String fontDir, + private static FontConfig.Font readFont( + @NonNull XmlPullParser parser, + @NonNull String fontDir, @Nullable Map<String, File> updatableFontMap) throws XmlPullParserException, IOException { + String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); - List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); + List<FontVariationAxis> axes = new ArrayList<>(); String weightStr = parser.getAttributeValue(null, "weight"); int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); @@ -144,12 +190,37 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - String fontName = findFontFile(sanitizedName, fontDir, updatableFontMap); - return new FontConfig.Font(fontName, index, axes.toArray( - new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); + String updatedName = findUpdatedFontFile(sanitizedName, updatableFontMap); + String filePath; + String originalPath; + if (updatedName != null) { + filePath = updatedName; + originalPath = fontDir + sanitizedName; + } else { + filePath = fontDir + sanitizedName; + originalPath = null; + } + + String varSettings; + if (axes.isEmpty()) { + varSettings = ""; + } else { + varSettings = FontVariationAxis.toFontVariationSettings( + axes.toArray(new FontVariationAxis[0])); + } + + return new FontConfig.Font(new File(filePath), + originalPath == null ? null : new File(originalPath), + new FontStyle( + weight, + isItalic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT + ), + index, + varSettings, + fallbackFor); } - private static String findFontFile(String name, String fontDir, + private static String findUpdatedFontFile(String name, @Nullable Map<String, File> updatableFontMap) { if (updatableFontMap != null) { File updatedFile = updatableFontMap.get(name); @@ -157,7 +228,7 @@ public class FontListParser { return updatedFile.getAbsolutePath(); } } - return fontDir + name; + return null; } private static FontVariationAxis readAxis(XmlPullParser parser) @@ -193,12 +264,12 @@ public class FontListParser { int depth = 1; while (depth > 0) { switch (parser.next()) { - case XmlPullParser.START_TAG: - depth++; - break; - case XmlPullParser.END_TAG: - depth--; - break; + case XmlPullParser.START_TAG: + depth++; + break; + case XmlPullParser.END_TAG: + depth--; + break; } } } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 48b474d6322e..f1866cdceae7 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -45,7 +45,6 @@ import android.text.FontConfig; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; -import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -197,7 +196,11 @@ public class Typeface { // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp /** @hide */ public static final int RESOLVE_BY_FONT_TABLE = -1; - private static final String DEFAULT_FAMILY = "sans-serif"; + /** + * The key of the default font family. + * @hide + */ + public static final String DEFAULT_FAMILY = "sans-serif"; // Style value for building typeface. private static final int STYLE_NORMAL = 0; @@ -1139,18 +1142,19 @@ public class Typeface { /** @hide */ @VisibleForTesting - public static void initSystemDefaultTypefaces(Map<String, Typeface> systemFontMap, - Map<String, FontFamily[]> fallbacks, - FontConfig.Alias[] aliases) { + public static void initSystemDefaultTypefaces(Map<String, FontFamily[]> fallbacks, + List<FontConfig.Alias> aliases, + Map<String, Typeface> outSystemFontMap) { for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) { - systemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue())); + outSystemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue())); } - for (FontConfig.Alias alias : aliases) { - if (systemFontMap.containsKey(alias.getName())) { + for (int i = 0; i < aliases.size(); ++i) { + final FontConfig.Alias alias = aliases.get(i); + if (outSystemFontMap.containsKey(alias.getAliasName())) { continue; // If alias and named family are conflict, use named family. } - final Typeface base = systemFontMap.get(alias.getToName()); + final Typeface base = outSystemFontMap.get(alias.getReferName()); if (base == null) { // The missing target is a valid thing, some configuration don't have font files, // e.g. wear devices. Just skip this alias. @@ -1159,7 +1163,7 @@ public class Typeface { final int weight = alias.getWeight(); final Typeface newFace = weight == 400 ? base : new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); - systemFontMap.put(alias.getName(), newFace); + outSystemFontMap.put(alias.getAliasName(), newFace); } } @@ -1339,11 +1343,11 @@ public class Typeface { /** @hide */ public static void loadPreinstalledSystemFontMap() { - final HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair = - SystemFonts.initializePreinstalledFonts(); - initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first); - setSystemFontMap(systemFontMap); + final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + setSystemFontMap(typefaceMap); } static { diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index f95da82ee07c..1ad6fbe264cb 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -16,18 +16,25 @@ package android.graphics.fonts; +import static android.text.FontConfig.Alias; +import static android.text.FontConfig.Family; + import android.annotation.NonNull; +import android.annotation.Nullable; 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.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Parser for font customization @@ -39,8 +46,27 @@ public class FontCustomizationParser { * Represents a customization XML */ public static class Result { - ArrayList<FontConfig.Family> mAdditionalNamedFamilies = new ArrayList<>(); - ArrayList<FontConfig.Alias> mAdditionalAliases = new ArrayList<>(); + private final Map<String, Family> mAdditionalNamedFamilies; + private final List<Alias> mAdditionalAliases; + + public Result() { + mAdditionalNamedFamilies = Collections.emptyMap(); + mAdditionalAliases = Collections.emptyList(); + } + + public Result(Map<String, Family> additionalNamedFamilies, + List<Alias> additionalAliases) { + mAdditionalNamedFamilies = additionalNamedFamilies; + mAdditionalAliases = additionalAliases; + } + + public Map<String, Family> getAdditionalNamedFamilies() { + return mAdditionalNamedFamilies; + } + + public List<Alias> getAdditionalAliases() { + return mAdditionalAliases; + } } /** @@ -48,56 +74,67 @@ public class FontCustomizationParser { * * Caller must close the input stream */ - public static Result parse(@NonNull InputStream in, @NonNull String fontDir) - throws XmlPullParserException, IOException { + public static Result parse( + @NonNull InputStream in, + @NonNull String fontDir, + @Nullable Map<String, File> updatableFontMap + ) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser, fontDir); + return readFamilies(parser, fontDir, updatableFontMap); } - 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(); + private static Map<String, Family> validateAndTransformToMap(List<Family> families) { + HashMap<String, Family> namedFamily = new HashMap<>(); + for (int i = 0; i < families.size(); ++i) { + final Family family = families.get(i); + final String name = family.getFallbackName(); if (name == null) { throw new IllegalArgumentException("new-named-family requires name attribute"); } - if (!familyNames.add(name)) { + if (namedFamily.put(name, family) != null) { throw new IllegalArgumentException( "new-named-family requires unique name attribute"); } } + return namedFamily; } - private static Result readFamilies(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { - Result out = new Result(); + private static Result readFamilies( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @Nullable Map<String, File> updatableFontMap + ) throws XmlPullParserException, IOException { + List<Family> families = new ArrayList<>(); + List<Alias> aliases = new ArrayList<>(); 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); + readFamily(parser, fontDir, families, updatableFontMap); } else if (tag.equals("alias")) { - out.mAdditionalAliases.add(FontListParser.readAlias(parser)); + aliases.add(FontListParser.readAlias(parser)); } else { FontListParser.skip(parser); } } - validate(out); - return out; + return new Result(validateAndTransformToMap(families), aliases); } - private static void readFamily(XmlPullParser parser, String fontDir, Result out) + private static void readFamily( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @NonNull List<Family> out, + @Nullable Map<String, File> updatableFontMap) 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, null)); + out.add(FontListParser.readFamily(parser, fontDir, updatableFontMap)); } else { throw new IllegalArgumentException("Unknown customizationType=" + customizationType); } diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index 75ea12062929..2f0c26f06df7 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -18,6 +18,7 @@ package android.graphics.fonts; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.text.FontConfig; import com.android.internal.util.Preconditions; @@ -119,7 +120,7 @@ public final class FontFamily { nAddFont(builderPtr, mFonts.get(i).getNativePtr()); } final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback); - final FontFamily family = new FontFamily(mFonts, ptr); + final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr); sFamilyRegistory.registerNativeAllocation(family, ptr); return family; } @@ -138,15 +139,36 @@ public final class FontFamily { } private final ArrayList<Font> mFonts; + private final String mLangTags; + private final int mVariant; private final long mNativePtr; // Use Builder instead. - private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) { + private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) { mFonts = fonts; + mLangTags = langTags; + mVariant = variant; mNativePtr = ptr; } /** + * Returns a BCP-47 compliant language tags associated with this font family. + * @hide + * @return a BCP-47 compliant language tag. + */ + public @Nullable String getLangTags() { + return mLangTags; + } + + /** + * @hide + * @return a family variant + */ + public int getVariant() { + return mVariant; + } + + /** * Returns a font * * @param index an index of the font diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 93b1fcc3ba1f..54167b4b3042 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -23,11 +23,9 @@ import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import org.xmlpull.v1.XmlPullParserException; @@ -37,7 +35,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +49,11 @@ public final class SystemFonts { private static final String TAG = "SystemFonts"; private static final String DEFAULT_FAMILY = "sans-serif"; + private static final String FONTS_XML = "/system/etc/fonts.xml"; + private static final String SYSTEM_FONT_DIR = "/system/fonts/"; + private static final String OEM_XML = "/product/etc/fonts_customization.xml"; + private static final String OEM_FONT_DIR = "/product/fonts/"; + private SystemFonts() {} // Do not instansiate. private static final Object LOCK = new Object(); @@ -88,12 +90,10 @@ public final class SystemFonts { } private static @NonNull Set<Font> collectAllFonts() { - final FontCustomizationParser.Result oemCustomization = - readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); - Map<String, FontFamily[]> map = new ArrayMap<>(); // TODO: use updated fonts - buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", null /* updatableFontMap */, - oemCustomization, map); + FontConfig fontConfig = getSystemPreinstalledFontConfig(); + Map<String, FontFamily[]> map = buildSystemFallback(fontConfig); + Set<Font> res = new HashSet<>(); for (FontFamily[] families : map.values()) { for (FontFamily family : families) { @@ -119,15 +119,16 @@ public final class SystemFonts { @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap, @NonNull Map<String, ByteBuffer> cache) { - final String languageTags = xmlFamily.getLanguages(); - final int variant = xmlFamily.getVariant(); + final String languageTags = xmlFamily.getLocaleList().toLanguageTags(); + final int variant = xmlFamily.getTextHeightVariant(); final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>(); - final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = new ArrayMap<>(); + final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = + new ArrayMap<>(); // Collect default fallback and specific fallback fonts. for (final FontConfig.Font font : xmlFamily.getFonts()) { - final String fallbackName = font.getFallbackFor(); + final String fallbackName = font.getFallback(); if (fallbackName == null) { defaultFonts.add(font); } else { @@ -141,19 +142,22 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getName(), defaultFonts, languageTags, variant, cache); + xmlFamily.getFallbackName(), defaultFonts, languageTags, variant, cache); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { - final ArrayList<FontConfig.Font> fallback = - specificFallbackFonts.get(fallbackMap.keyAt(i)); + String name = fallbackMap.keyAt(i); + final ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(name); if (fallback == null) { - if (defaultFamily != null) { + String familyName = xmlFamily.getFallbackName(); + if (defaultFamily != null + // do not add myself to the fallback chain. + && (familyName == null || !familyName.equals(name))) { fallbackMap.valueAt(i).add(defaultFamily); } } else { final FontFamily family = createFontFamily( - xmlFamily.getName(), fallback, languageTags, variant, cache); + xmlFamily.getFallbackName(), fallback, languageTags, variant, cache); if (family != null) { fallbackMap.valueAt(i).add(family); } else if (defaultFamily != null) { @@ -177,7 +181,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 = fontConfig.getFontName(); + final String fullPath = fontConfig.getFilePath().getAbsolutePath(); ByteBuffer buffer = cache.get(fullPath); if (buffer == null) { if (cache.containsKey(fullPath)) { @@ -193,11 +197,10 @@ public final class SystemFonts { final Font font; try { font = new Font.Builder(buffer, new File(fullPath), languageTags) - .setWeight(fontConfig.getWeight()) - .setSlant(fontConfig.isItalic() ? FontStyle.FONT_SLANT_ITALIC - : FontStyle.FONT_SLANT_UPRIGHT) - .setTtcIndex(fontConfig.getTtcIndex()) - .setFontVariationSettings(fontConfig.getAxes()) + .setWeight(fontConfig.getStyle().getWeight()) + .setSlant(fontConfig.getStyle().getSlant()) + .setTtcIndex(fontConfig.getIndex()) + .setFontVariationSettings(fontConfig.getFontVariationSettings()) .build(); } catch (IOException e) { throw new RuntimeException(e); // Never reaches here @@ -215,10 +218,11 @@ public final class SystemFonts { private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, @NonNull HashMap<String, ByteBuffer> bufferCache, @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) { - final String familyName = xmlFamily.getName(); + final String familyName = xmlFamily.getFallbackName(); final FontFamily family = createFontFamily( - familyName, Arrays.asList(xmlFamily.getFonts()), - xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache); + familyName, xmlFamily.getFontList(), + xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getTextHeightVariant(), + bufferCache); if (family == null) { return; } @@ -228,115 +232,104 @@ public final class SystemFonts { } /** - * @see #buildSystemFallback(String, String, Map, FontCustomizationParser.Result, Map) + * Get the updated FontConfig. + * + * @param updatableFontMap a font mapping of updated font files. * @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 /* updatableFontMap */, - oemCustomization, fallbackMap); + public static @NonNull FontConfig getSystemFontConfig( + @Nullable Map<String, File> updatableFontMap + ) { + return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, + updatableFontMap); } /** - * 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 updatableFontMap A map from font file name to updated font file path. - * @param fallbackMap An output system fallback map. Caller must pass empty map. - * @return a list of aliases + * Get the system preinstalled FontConfig. * @hide */ - @VisibleForTesting - public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, - @NonNull String fontDir, - @Nullable Map<String, File> updatableFontMap, - @NonNull FontCustomizationParser.Result oemCustomization, - @NonNull Map<String, FontFamily[]> fallbackMap) { - try { - final FileInputStream fontsIn = new FileInputStream(xmlPath); - final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir, updatableFontMap); - - final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>(); - final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); - - final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); - // First traverse families which have a 'name' attribute to create fallback map. - for (final FontConfig.Family xmlFamily : xmlFamilies) { - final String familyName = xmlFamily.getName(); - if (familyName == null) { - continue; - } - appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); - } + public static @NonNull FontConfig getSystemPreinstalledFontConfig() { + return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null); + } - for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) { - appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i), - bufferCache, fallbackListMap); - } + /* package */ static @NonNull FontConfig getSystemFontConfigInternal( + @NonNull String fontsXml, + @NonNull String systemFontDir, + @Nullable String oemXml, + @Nullable String productFontDir, + @Nullable Map<String, File> updatableFontMap + ) { + try { + return FontListParser.parse(fontsXml, systemFontDir, oemXml, productFontDir, + updatableFontMap); + } catch (IOException e) { + Log.e(TAG, "Failed to open/read system font configurations.", e); + return new FontConfig(Collections.emptyList(), Collections.emptyList()); + } catch (XmlPullParserException e) { + Log.e(TAG, "Failed to parse the system font configuration.", e); + return new FontConfig(Collections.emptyList(), Collections.emptyList()); + } + } - // Then, add fallback fonts to the each fallback map. - for (int i = 0; i < xmlFamilies.length; i++) { - final FontConfig.Family xmlFamily = xmlFamilies[i]; - // 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); - } + /** + * Build the system fallback from FontConfig. + * @hide + */ + @VisibleForTesting + public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) { + final Map<String, FontFamily[]> fallbackMap = new HashMap<>(); + final HashMap<String, ByteBuffer> bufferCache = new HashMap<>(); + final List<FontConfig.Family> xmlFamilies = fontConfig.getFontFamilies(); + + final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); + // First traverse families which have a 'name' attribute to create fallback map. + for (final FontConfig.Family xmlFamily : xmlFamilies) { + final String familyName = xmlFamily.getFallbackName(); + if (familyName == null) { + continue; } + appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); + } - // Build the font map and fallback map. - for (int i = 0; i < fallbackListMap.size(); i++) { - final String fallbackName = fallbackListMap.keyAt(i); - final List<FontFamily> familyList = fallbackListMap.valueAt(i); - final FontFamily[] families = familyList.toArray(new FontFamily[familyList.size()]); - - fallbackMap.put(fallbackName, families); + // Then, add fallback fonts to the each fallback map. + for (int i = 0; i < xmlFamilies.size(); i++) { + final FontConfig.Family xmlFamily = xmlFamilies.get(i); + // The first family (usually the sans-serif family) is always placed immediately + // after the primary family in the fallback. + if (i == 0 || xmlFamily.getFallbackName() == null) { + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); } - - 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(); + // Build the font map and fallback map. + for (int i = 0; i < fallbackListMap.size(); i++) { + final String fallbackName = fallbackListMap.keyAt(i); + final List<FontFamily> familyList = fallbackListMap.valueAt(i); + fallbackMap.put(fallbackName, familyList.toArray(new FontFamily[0])); } + + return fallbackMap; } - /** @hide */ - public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>> - initializePreinstalledFonts() { - return initializeSystemFonts(null); + /** + * Build the system Typeface mappings from FontConfig and FallbackMap. + * @hide + */ + @VisibleForTesting + public static Map<String, Typeface> buildSystemTypefaces( + FontConfig fontConfig, + Map<String, FontFamily[]> fallbackMap) { + final HashMap<String, Typeface> result = new HashMap<>(); + Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result); + return result; } - /** @hide */ - public static Pair<FontConfig.Alias[], Map<String, FontFamily[]>> - initializeSystemFonts(@Nullable Map<String, File> updatableFontMap) { - 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/", - updatableFontMap, oemCustomization, map); + /** + * @hide + */ + public void resetFallbackMapping(Map<String, FontFamily[]> fallbackMap) { synchronized (LOCK) { - sFamilyMap = map; + sFamilyMap = fallbackMap; } - return new Pair(aliases, map); } } diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 633c0c417192..a0d9e8e0a6fb 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -26,7 +26,6 @@ import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import android.system.ErrnoException; import android.text.FontConfig; -import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -40,7 +39,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.NioUtils; import java.nio.channels.FileChannel; -import java.util.HashMap; import java.util.Map; /** A service for managing system fonts. */ @@ -69,7 +67,10 @@ public final class FontManagerService { @Override @Nullable public SharedMemory getSerializedSystemFontMap() { - return mService.getSerializedSystemFontMap(); + if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + return null; + } + return mService.getCurrentFontSettings().getSerializedSystemFontMap(); } }); } @@ -98,48 +99,20 @@ public final class FontManagerService { private final UpdatableFontDir mUpdatableFontDir; @GuardedBy("FontManagerService.this") - @Nullable - private SharedMemory mSerializedSystemFontMap = null; + @Nullable SystemFontSettings mCurrentFontSettings = null; private FontManagerService() { mUpdatableFontDir = ENABLE_FONT_UPDATES ? new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser()) : null; } - @Nullable - private SharedMemory getSerializedSystemFontMap() { - if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { - return null; - } + @NonNull private SystemFontSettings getCurrentFontSettings() { synchronized (FontManagerService.this) { - if (mSerializedSystemFontMap == null) { - mSerializedSystemFontMap = createSerializedSystemFontMapLocked(); - } - return mSerializedSystemFontMap; - } - } - - @Nullable - private SharedMemory createSerializedSystemFontMapLocked() { - if (mUpdatableFontDir != null) { - HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Map<String, File> fontFileMap = mUpdatableFontDir.getFontFileMap(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair = - SystemFonts.initializeSystemFonts(fontFileMap); - Typeface.initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first); - try { - return Typeface.serializeFontMap(systemFontMap); - } catch (IOException | ErrnoException e) { - Slog.w(TAG, "Failed to serialize updatable font map. " - + "Retrying with system image fonts.", e); + if (mCurrentFontSettings == null) { + mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir); } + return mCurrentFontSettings; } - try { - return Typeface.serializeFontMap(Typeface.getSystemFontMap()); - } catch (IOException | ErrnoException e) { - Slog.e(TAG, "Failed to serialize SystemServer system font map", e); - } - return null; } private boolean installFontFile(String name, FileDescriptor fd) { @@ -152,8 +125,74 @@ public final class FontManagerService { return false; } // Create updated font map in the next getSerializedSystemFontMap() call. - mSerializedSystemFontMap = null; + mCurrentFontSettings = null; return true; } } + + private static class SystemFontSettings { + private final @NonNull SharedMemory mSerializedSystemFontMap; + private final @NonNull FontConfig mSystemFontConfig; + private final @NonNull Map<String, FontFamily[]> mSystemFallbackMap; + private final @NonNull Map<String, Typeface> mSystemTypefaceMap; + + SystemFontSettings( + @NonNull SharedMemory serializedSystemFontMap, + @NonNull FontConfig systemFontConfig, + @NonNull Map<String, FontFamily[]> systemFallbackMap, + @NonNull Map<String, Typeface> systemTypefaceMap) { + mSerializedSystemFontMap = serializedSystemFontMap; + mSystemFontConfig = systemFontConfig; + mSystemFallbackMap = systemFallbackMap; + mSystemTypefaceMap = systemTypefaceMap; + } + + public @NonNull SharedMemory getSerializedSystemFontMap() { + return mSerializedSystemFontMap; + } + + public @NonNull FontConfig getSystemFontConfig() { + return mSystemFontConfig; + } + + public @NonNull Map<String, FontFamily[]> getSystemFallbackMap() { + return mSystemFallbackMap; + } + + public @NonNull Map<String, Typeface> getSystemTypefaceMap() { + return mSystemTypefaceMap; + } + + public static @Nullable SystemFontSettings create( + @Nullable UpdatableFontDir updatableFontDir) { + if (updatableFontDir != null) { + final FontConfig fontConfig = SystemFonts.getSystemFontConfig( + updatableFontDir.getFontFileMap()); + final Map<String, FontFamily[]> fallback = + SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + + try { + final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); + return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.w(TAG, "Failed to serialize updatable font map. " + + "Retrying with system image fonts.", e); + } + } + + final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + try { + final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); + return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to serialize SystemServer system font map", e); + } + return null; + } + }; } |