Merge "Introduce system font enumeration API"
diff --git a/api/current.txt b/api/current.txt
index 872d0ec..88d7825 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -15218,6 +15218,9 @@
public final class Font {
method public android.graphics.fonts.FontVariationAxis[] getAxes();
+ method public java.nio.ByteBuffer getBuffer();
+ method public java.io.File getFile();
+ method public android.os.LocaleList getLocaleList();
method public int getTtcIndex();
method public int getWeight();
method public boolean isItalic();
@@ -15266,6 +15269,10 @@
method public static java.lang.String toFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
}
+ public class SystemFonts {
+ method public static java.util.Set<android.graphics.fonts.Font> getAvailableFonts();
+ }
+
}
package android.graphics.pdf {
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 9e0fee33..19c55d5 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -181,7 +181,8 @@
public static final class Family {
private final @NonNull String mName;
private final @NonNull Font[] mFonts;
- private final @NonNull String[] mLanguages;
+ // Comma separated BCP47 complient locale strings
+ private final @NonNull String mLanguages;
/** @hide */
@Retention(SOURCE)
@@ -219,7 +220,7 @@
// See frameworks/minikin/include/minikin/FontFamily.h
private final @Variant int mVariant;
- public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String[] languages,
+ public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String languages,
@Variant int variant) {
mName = name;
mFonts = fonts;
@@ -244,9 +245,9 @@
}
/**
- * Returns the languages for this family. May be null.
+ * Returns the comma separated BCP47 complient languages for this family. May be null.
*/
- public @Nullable String[] getLanguages() {
+ public @NonNull String getLanguages() {
return mLanguages;
}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index dd34f1f..1c4039b 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.support.test.InstrumentationRegistry;
@@ -43,6 +44,7 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.util.HashSet;
import java.util.Locale;
@SmallTest
@@ -110,13 +112,14 @@
private static void buildSystemFallback(String xml,
ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
+ final HashSet<Font> availableFonts = new HashSet<>();
try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) {
fos.write(xml.getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
throw new RuntimeException(e);
}
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML,
- TEST_FONT_DIR, fallbackMap);
+ TEST_FONT_DIR, fallbackMap, availableFonts);
Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases);
}
@@ -124,9 +127,10 @@
public void testBuildSystemFallback() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final HashSet<Font> availableFonts = new HashSet<>();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML,
- SYSTEM_FONT_DIR, fallbackMap);
+ SYSTEM_FONT_DIR, fallbackMap, availableFonts);
assertNotNull(aliases);
assertFalse(fallbackMap.isEmpty());
@@ -487,7 +491,7 @@
+ " <family lang='de'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
- + " <family lang='it fr'>"
+ + " <family lang='it,fr'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</familyset>";
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index 6c34043..355be61 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Typeface;
+import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.support.test.InstrumentationRegistry;
@@ -32,6 +33,7 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.util.HashSet;
public class FontFallbackSetup implements AutoCloseable {
private final String[] mTestFontFiles;
@@ -74,8 +76,9 @@
}
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final HashSet<Font> availableFonts = new HashSet<>();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml,
- mTestFontsDir, fallbackMap);
+ mTestFontsDir, fallbackMap, availableFonts);
Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases);
}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index be9347a..c84c035 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -137,7 +137,7 @@
<font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.otf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Bold.otf</font>
</family>
- <family lang="und-Geor und-Geok">
+ <family lang="und-Geor,und-Geok">
<font weight="400" style="normal">NotoSansGeorgian-Regular.otf</font>
<font weight="500" style="normal">NotoSansGeorgian-Medium.otf</font>
<font weight="700" style="normal">NotoSansGeorgian-Bold.otf</font>
@@ -538,7 +538,7 @@
<font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
<font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
- <family lang="zh-Hant zh-Bopo">
+ <family lang="zh-Hant,zh-Bopo">
<font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
<font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index e3e8380..82435d5 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -74,8 +74,7 @@
private static FontConfig.Family readFamily(XmlPullParser parser)
throws XmlPullParserException, IOException {
final String name = parser.getAttributeValue(null, "name");
- final String lang = parser.getAttributeValue(null, "lang");
- final String[] langs = lang == null ? null : lang.split("\\s+");
+ final String lang = parser.getAttributeValue("", "lang");
final String variant = parser.getAttributeValue(null, "variant");
final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
while (parser.next() != XmlPullParser.END_TAG) {
@@ -95,7 +94,7 @@
intVariant = FontConfig.Family.VARIANT_ELEGANT;
}
}
- return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), langs,
+ return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang,
intVariant);
}
@@ -126,8 +125,8 @@
}
}
String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
- return new FontConfig.Font(sanitizedName, index,
- axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
+ return new FontConfig.Font(sanitizedName, index, axes.toArray(
+ new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
}
private static FontVariationAxis readAxis(XmlPullParser parser)
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index f0c5199..a99d297 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.os.LocaleList;
import android.util.TypedValue;
import com.android.internal.util.Preconditions;
@@ -107,6 +108,8 @@
nGetReleaseNativeFont(), 64);
private @Nullable ByteBuffer mBuffer;
+ private @Nullable File mFile;
+ private @NonNull LocaleList mLocaleList = LocaleList.getEmptyLocaleList();
private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED;
private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED;
private @IntRange(from = 0) int mTtcIndex = 0;
@@ -131,6 +134,19 @@
}
/**
+ * Construct a builder with a byte buffer and file path.
+ *
+ * This method is intended to be called only from SystemFonts.
+ * @hide
+ */
+ public Builder(@NonNull ByteBuffer buffer, @NonNull File path,
+ @NonNull LocaleList localeList) {
+ this(buffer);
+ mFile = path;
+ mLocaleList = localeList;
+ }
+
+ /**
* Constructs a builder with a file path.
*
* @param path a file path to the font file
@@ -143,6 +159,7 @@
} catch (IOException e) {
mException = e;
}
+ mFile = path;
}
/**
@@ -394,7 +411,8 @@
}
}
final long ptr = nBuild(builderPtr, mBuffer, mWeight, italic, mTtcIndex);
- final Font font = new Font(ptr, mBuffer, mWeight, italic, mTtcIndex, mAxes);
+ final Font font = new Font(ptr, mBuffer, mFile, mWeight, italic, mTtcIndex, mAxes,
+ mLocaleList);
sFontRegistory.registerNativeAllocation(font, ptr);
return font;
}
@@ -422,23 +440,48 @@
private final long mNativePtr; // address of the shared ptr of minikin::Font
private final @NonNull ByteBuffer mBuffer;
+ private final @Nullable File mFile;
private final @IntRange(from = 0, to = 1000) int mWeight;
private final boolean mItalic;
private final @IntRange(from = 0) int mTtcIndex;
private final @Nullable FontVariationAxis[] mAxes;
+ private final @NonNull LocaleList mLocaleList;
/**
* Use Builder instead
*/
- private Font(long nativePtr, @NonNull ByteBuffer buffer,
+ private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
@IntRange(from = 0, to = 1000) int weight, boolean italic,
- @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes) {
+ @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes,
+ @NonNull LocaleList localeList) {
mBuffer = buffer;
+ mFile = file;
mWeight = weight;
mItalic = italic;
mNativePtr = nativePtr;
mTtcIndex = ttcIndex;
mAxes = axes;
+ mLocaleList = localeList;
+ }
+
+ /**
+ * Retuns a font file buffer.
+ *
+ * @return a font buffer
+ */
+ public @NonNull ByteBuffer getBuffer() {
+ return mBuffer;
+ }
+
+ /**
+ * Returns a file path of this font.
+ *
+ * This returns null if this font is not created from regular file.
+ *
+ * @return a file path of the font
+ */
+ public @Nullable File getFile() {
+ return mFile;
}
/**
@@ -484,6 +527,16 @@
return mAxes == null ? null : mAxes.clone();
}
+ /**
+ * Get a locale list of this font.
+ *
+ * This is always empty if this font is not a system font.
+ * @return a locale list
+ */
+ public @NonNull LocaleList getLocaleList() {
+ return mLocaleList;
+ }
+
/** @hide */
public long getNativePtr() {
return mNativePtr;
@@ -504,11 +557,19 @@
@Override
public int hashCode() {
- return Objects.hash(mWeight, mItalic, mTtcIndex, mAxes, mBuffer);
+ return Objects.hash(mWeight, mItalic, mTtcIndex, Arrays.hashCode(mAxes), mBuffer);
}
@Override
public String toString() {
- return "Font {weight=" + mWeight + ", italic=" + mItalic + "}";
+ return "Font {"
+ + "path=" + mFile
+ + ", weight=" + mWeight
+ + ", italic=" + mItalic
+ + ", ttcIndex=" + mTtcIndex
+ + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
+ + ", localeList=" + mLocaleList.toLanguageTags()
+ + ", buffer=" + mBuffer
+ + "}";
}
}
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index c914ece..3bcdc31 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -18,9 +18,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.text.FontConfig;
-import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -110,25 +108,16 @@
* @return a font family
*/
public @NonNull FontFamily build() {
- return build(null, FontConfig.Family.VARIANT_DEFAULT);
+ return build("", FontConfig.Family.VARIANT_DEFAULT);
}
/** @hide */
- public @NonNull FontFamily build(@Nullable String[] langTags, int variant) {
+ public @NonNull FontFamily build(@NonNull String langTags, int variant) {
final long builderPtr = nInitBuilder();
for (int i = 0; i < mFonts.size(); ++i) {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
- final String langString;
- if (langTags == null || langTags.length == 0) {
- langString = null;
- } else if (langTags.length == 1) {
- langString = langTags[0];
- } else {
- langString = TextUtils.join(",", langTags);
- }
-
- final long ptr = nBuild(builderPtr, langString, variant);
+ final long ptr = nBuild(builderPtr, langTags, variant);
final FontFamily family = new FontFamily(mFonts, ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 26b4ec5..1c957b8 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
+import android.os.LocaleList;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -28,6 +29,7 @@
import org.xmlpull.v1.XmlPullParserException;
+import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -36,12 +38,13 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Provides the system font configurations.
- * @hide
*/
public class SystemFonts {
private static final String TAG = "SystemFonts";
@@ -49,8 +52,19 @@
private SystemFonts() {} // Do not instansiate.
- static final Map<String, FontFamily[]> sSystemFallbackMap;
- static final FontConfig.Alias[] sAliases;
+ private static final Map<String, FontFamily[]> sSystemFallbackMap;
+ private static final FontConfig.Alias[] sAliases;
+ private static final Set<Font> sAvailableFonts;
+
+ /**
+ * Returns all available font files in the system.
+ *
+ * Note: The order of this font doesn't indicates anything.
+ * @return an array of system fonts
+ */
+ public static @NonNull Set<Font> getAvailableFonts() {
+ return sAvailableFonts;
+ }
/**
* Returns fallback list for the given family name.
@@ -58,6 +72,7 @@
* If no fallback found for the given family name, returns fallback for the default family.
*
* @param familyName family name, e.g. "serif"
+ * @hide
*/
public static @NonNull FontFamily[] getSystemFallback(@Nullable String familyName) {
final FontFamily[] families = sSystemFallbackMap.get(familyName);
@@ -68,6 +83,7 @@
* Returns raw system fallback map.
*
* This method is intended to be used only by Typeface static initializer.
+ * @hide
*/
public static @NonNull Map<String, FontFamily[]> getRawSystemFallbackMap() {
return sSystemFallbackMap;
@@ -77,6 +93,7 @@
* Returns a list of aliases.
*
* This method is intended to be used only by Typeface static initializer.
+ * @hide
*/
public static @NonNull FontConfig.Alias[] getAliases() {
return sAliases;
@@ -96,9 +113,10 @@
private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
@NonNull Map<String, ByteBuffer> cache,
- @NonNull String fontDir) {
+ @NonNull String fontDir,
+ @NonNull HashSet<Font> availableFonts) {
- final String[] languageTags = xmlFamily.getLanguages();
+ final String languageTags = xmlFamily.getLanguages();
final int variant = xmlFamily.getVariant();
final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>();
@@ -120,7 +138,8 @@
}
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
- xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir);
+ xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir,
+ availableFonts);
// Insert family into fallback map.
for (int i = 0; i < fallbackMap.size(); i++) {
@@ -132,7 +151,8 @@
}
} else {
final FontFamily family = createFontFamily(
- xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir);
+ xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir,
+ availableFonts);
if (family != null) {
fallbackMap.valueAt(i).add(family);
} else if (defaultFamily != null) {
@@ -146,15 +166,17 @@
private static @Nullable FontFamily createFontFamily(@NonNull String familyName,
@NonNull List<FontConfig.Font> fonts,
- @NonNull String[] languageTags,
+ @NonNull String languageTags,
@FontConfig.Family.Variant int variant,
@NonNull Map<String, ByteBuffer> cache,
- @NonNull String fontDir) {
+ @NonNull String fontDir,
+ @NonNull HashSet<Font> availableFonts) {
if (fonts.size() == 0) {
return null;
}
FontFamily.Builder b = null;
+ final LocaleList localeList = LocaleList.forLanguageTags(languageTags);
for (int i = 0; i < fonts.size(); i++) {
final FontConfig.Font fontConfig = fonts.get(i);
final String fullPath = fontDir + fontConfig.getFontName();
@@ -172,7 +194,7 @@
final Font font;
try {
- font = new Font.Builder(buffer)
+ font = new Font.Builder(buffer, new File(fullPath), localeList)
.setWeight(fontConfig.getWeight())
.setItalic(fontConfig.isItalic())
.setTtcIndex(fontConfig.getTtcIndex())
@@ -182,6 +204,7 @@
throw new RuntimeException(e); // Never reaches here
}
+ availableFonts.add(font);
if (b == null) {
b = new FontFamily.Builder(font);
} else {
@@ -204,7 +227,8 @@
@VisibleForTesting
public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath,
@NonNull String fontDir,
- @NonNull ArrayMap<String, FontFamily[]> fallbackMap) {
+ @NonNull ArrayMap<String, FontFamily[]> fallbackMap,
+ @NonNull HashSet<Font> availableFonts) {
try {
final FileInputStream fontsIn = new FileInputStream(xmlPath);
final FontConfig fontConfig = FontListParser.parse(fontsIn);
@@ -221,7 +245,8 @@
}
final FontFamily family = createFontFamily(
xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
- xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir);
+ xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir,
+ availableFonts);
if (family == null) {
continue;
}
@@ -236,7 +261,8 @@
// The first family (usually the sans-serif family) is always placed immediately
// after the primary family in the fallback.
if (i == 0 || xmlFamily.getName() == null) {
- pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir);
+ pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir,
+ availableFonts);
}
}
@@ -258,9 +284,11 @@
static {
final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
+ final HashSet<Font> availableFonts = new HashSet<>();
sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/",
- systemFallbackMap);
+ systemFallbackMap, availableFonts);
sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
+ sAvailableFonts = Collections.unmodifiableSet(availableFonts);
}
}