Merge "Refactor font parser for making SystemApi."
This commit is contained in:
committed by
Android (Google) Code Review
commit
903dc9269f
@@ -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 the name associated by the system to this font.
|
||||
* Returns a file to the font file.
|
||||
*
|
||||
* @return a font file.
|
||||
*/
|
||||
public @NonNull String getFontName() {
|
||||
return mFontName;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a font variation settings.
|
||||
*
|
||||
* @return a font variation settings.
|
||||
*/
|
||||
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,26 +309,81 @@ 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.
|
||||
*/
|
||||
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 a locale list if associated.
|
||||
*
|
||||
* @return non-null if a locale list is associated. Otherwise null.
|
||||
*/
|
||||
public @NonNull LocaleList getLocaleList() {
|
||||
return mLocaleList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a text height variant.
|
||||
*
|
||||
* @return text height variant.
|
||||
*/
|
||||
public @Variant int getTextHeightVariant() {
|
||||
return mVariant;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -239,25 +391,23 @@ public final class FontConfig {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return mFonts.toArray(new Font[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comma separated BCP47 complient languages for this family. May be null.
|
||||
* Returns the comma separated BCP47 compliant languages for this family. May be null.
|
||||
* @deprecated Use getLocaleList instead
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
public @NonNull String getLanguages() {
|
||||
return mLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the font variant for this family, e.g. "elegant" or "compact". May be null.
|
||||
*/
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
public @Variant int getVariant() {
|
||||
return mVariant;
|
||||
return mLocaleList.toLanguageTags();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
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());
|
||||
|
||||
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML,
|
||||
SYSTEM_FONT_DIR, oemCustomization, fallbackMap);
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,14 +139,35 @@ 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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
public static @NonNull FontConfig getSystemPreinstalledFontConfig() {
|
||||
return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null);
|
||||
}
|
||||
|
||||
/* 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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the system fallback from 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);
|
||||
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 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);
|
||||
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;
|
||||
}
|
||||
|
||||
for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) {
|
||||
appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i),
|
||||
bufferCache, fallbackListMap);
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
}
|
||||
|
||||
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);
|
||||
appendNamedFamily(xmlFamily, bufferCache, fallbackListMap);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,50 +99,22 @@ 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();
|
||||
if (mCurrentFontSettings == null) {
|
||||
mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir);
|
||||
}
|
||||
return mSerializedSystemFontMap;
|
||||
return mCurrentFontSettings;
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (mUpdatableFontDir == null) return false;
|
||||
synchronized (FontManagerService.this) {
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user