diff options
author | Seigo Nonaka <nona@google.com> | 2018-08-09 14:52:43 -0700 |
---|---|---|
committer | Seigo Nonaka <nona@google.com> | 2018-09-05 14:54:12 -0700 |
commit | c7b3e62f0d9a66c69d2e4b335e77f205826459a9 (patch) | |
tree | c8b14c659a9d43f868716b2e7237537ddf0f28fb | |
parent | 900fa92042ba1f1e28280f4df1231b3ede65274d (diff) |
Introduce system font enumeration API
To be able to use font file in their apps, provides blob and path to the
font file and locale list as well.
Bug: 26116537
Test: atest CtsWidgetTestCases:EditTextTest
CtsWidgetTestCases:TextViewFadingEdgeTest
FrameworksCoreTests:TextViewFallbackLineSpacingTest
FrameworksCoreTests:TextViewTest FrameworksCoreTests:TypefaceTest
CtsGraphicsTestCases:TypefaceTest CtsWidgetTestCases:TextViewTest
CtsTextTestCases FrameworksCoreTests:android.text
CtsWidgetTestCases:TextViewPrecomputedTextTest
CtsGraphicsTestCases:android.graphics.font
Change-Id: I1ae1302c6906b808012e1e91b1e4ab393c887cb6
-rwxr-xr-x | api/current.txt | 7 | ||||
-rw-r--r-- | core/java/android/text/FontConfig.java | 9 | ||||
-rw-r--r-- | core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java | 10 | ||||
-rw-r--r-- | core/tests/coretests/src/android/text/FontFallbackSetup.java | 5 | ||||
-rw-r--r-- | data/fonts/fonts.xml | 4 | ||||
-rw-r--r-- | graphics/java/android/graphics/FontListParser.java | 9 | ||||
-rw-r--r-- | graphics/java/android/graphics/fonts/Font.java | 71 | ||||
-rw-r--r-- | graphics/java/android/graphics/fonts/FontFamily.java | 17 | ||||
-rw-r--r-- | graphics/java/android/graphics/fonts/SystemFonts.java | 56 |
9 files changed, 140 insertions, 48 deletions
diff --git a/api/current.txt b/api/current.txt index 47a0811ef16c..6e86da4232f6 100755 --- a/api/current.txt +++ b/api/current.txt @@ -15212,6 +15212,9 @@ package android.graphics.fonts { 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(); @@ -15260,6 +15263,10 @@ package android.graphics.fonts { 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 9e0fee337bc7..19c55d5f497d 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -181,7 +181,8 @@ public final class FontConfig { 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 @@ public final class FontConfig { // 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 @@ public final class FontConfig { } /** - * 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 dd34f1fdde1f..1c4039b85441 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; 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.io.InputStream; 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 @@ public class TypefaceSystemFallbackTest { 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 class TypefaceSystemFallbackTest { 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 @@ public class TypefaceSystemFallbackTest { + " <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 6c34043c1995..355be619ea91 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.annotation.NonNull; 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.io.InputStream; 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 @@ public class FontFallbackSetup implements AutoCloseable { } 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 be9347ab6771..c84c0354a7c4 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 e3e8380716f3..82435d5511f5 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -74,8 +74,7 @@ public class FontListParser { 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 @@ public class FontListParser { 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 @@ public class FontListParser { } } 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 f0c519922fdd..a99d297cbd92 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; 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 @@ public final class Font { 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 @@ public final class Font { } /** + * 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 @@ public final class Font { } catch (IOException e) { mException = e; } + mFile = path; } /** @@ -394,7 +411,8 @@ public final class Font { } } 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 @@ public final class Font { 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 @@ public final class Font { 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 @@ public final class Font { @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 c914ecedd761..3bcdc31a3160 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -18,9 +18,7 @@ package android.graphics.fonts; 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 @@ public final class FontFamily { * @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 26b4ec530072..1c957b8880b9 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -19,6 +19,7 @@ package android.graphics.fonts; 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 com.android.internal.util.ArrayUtils; 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.ArrayList; 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 @@ public class SystemFonts { 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 @@ public class SystemFonts { * 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 @@ public class SystemFonts { * 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 @@ public class SystemFonts { * 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 @@ public class SystemFonts { 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 @@ public class SystemFonts { } 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 @@ public class SystemFonts { } } 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 @@ public class SystemFonts { 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 @@ public class SystemFonts { 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 @@ public class SystemFonts { throw new RuntimeException(e); // Never reaches here } + availableFonts.add(font); if (b == null) { b = new FontFamily.Builder(font); } else { @@ -204,7 +227,8 @@ public class SystemFonts { @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 @@ public class SystemFonts { } 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 @@ public class SystemFonts { // 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 @@ public class SystemFonts { 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); } } |