Add family-list tag element to fonts_customization.xml
family-list can be used as a named font family definition.
Multiple families can be used as a fallback for the named
family.
Bug: 249787583
Test: atest TypefaceSystemFallbackTest FontListParserTest
Test: atest UpdatableFontDirTest UpdatableSystemFontTest
Test: atest GtsFontHostTestCases FontManagerTest
Change-Id: Ic459a533ac4b5081660c0a4a7519ef7e87a6b628
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 566ac45..c5b7a71 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -16178,6 +16178,7 @@
method @IntRange(from=0) public int getConfigVersion();
method @NonNull public java.util.List<android.text.FontConfig.FontFamily> getFontFamilies();
method public long getLastModifiedTimeMillis();
+ method @NonNull public java.util.List<android.text.FontConfig.NamedFamilyList> getNamedFamilyLists();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
}
@@ -16207,7 +16208,7 @@
method public int describeContents();
method @NonNull public java.util.List<android.text.FontConfig.Font> getFontList();
method @NonNull public android.os.LocaleList getLocaleList();
- method @Nullable public String getName();
+ method @Deprecated @Nullable public String getName();
method public int getVariant();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.FontFamily> CREATOR;
@@ -16216,6 +16217,14 @@
field public static final int VARIANT_ELEGANT = 2; // 0x2
}
+ public static final class FontConfig.NamedFamilyList implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.text.FontConfig.FontFamily> getFamilies();
+ method @NonNull public String getName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.NamedFamilyList> CREATOR;
+ }
+
}
package android.util {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5e02e72..5238c5d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2706,6 +2706,7 @@
method @IntRange(from=0) public int getConfigVersion();
method @NonNull public java.util.List<android.text.FontConfig.FontFamily> getFontFamilies();
method public long getLastModifiedTimeMillis();
+ method @NonNull public java.util.List<android.text.FontConfig.NamedFamilyList> getNamedFamilyLists();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
}
@@ -2735,7 +2736,7 @@
method public int describeContents();
method @NonNull public java.util.List<android.text.FontConfig.Font> getFontList();
method @NonNull public android.os.LocaleList getLocaleList();
- method @Nullable public String getName();
+ method @Deprecated @Nullable public String getName();
method public int getVariant();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.FontFamily> CREATOR;
@@ -2744,6 +2745,14 @@
field public static final int VARIANT_ELEGANT = 2; // 0x2
}
+ public static final class FontConfig.NamedFamilyList implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.text.FontConfig.FontFamily> getFamilies();
+ method @NonNull public String getName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.NamedFamilyList> CREATOR;
+ }
+
public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher {
ctor public Selection.MemoryTextWatcher();
method public void afterTextChanged(android.text.Editable);
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 32b3bc6..cb488b0 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -36,6 +36,7 @@
import java.io.File;
import java.lang.annotation.Retention;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -55,6 +56,7 @@
public final class FontConfig implements Parcelable {
private final @NonNull List<FontFamily> mFamilies;
private final @NonNull List<Alias> mAliases;
+ private final @NonNull List<NamedFamilyList> mNamedFamilyLists;
private final long mLastModifiedTimeMillis;
private final int mConfigVersion;
@@ -67,14 +69,25 @@
* @hide Only system server can create this instance and passed via IPC.
*/
public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases,
+ @NonNull List<NamedFamilyList> namedFamilyLists,
long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) {
mFamilies = families;
mAliases = aliases;
+ mNamedFamilyLists = namedFamilyLists;
mLastModifiedTimeMillis = lastModifiedTimeMillis;
mConfigVersion = configVersion;
}
/**
+ * @hide Keep this constructor for reoborectric.
+ */
+ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases,
+ long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) {
+ this(families, aliases, Collections.emptyList(), lastModifiedTimeMillis, configVersion);
+ }
+
+
+ /**
* Returns the ordered list of font families available in the system.
*
* @return a list of font families.
@@ -94,6 +107,10 @@
return mAliases;
}
+ public @NonNull List<NamedFamilyList> getNamedFamilyLists() {
+ return mNamedFamilyLists;
+ }
+
/**
* Returns the last modified time in milliseconds.
*
@@ -133,8 +150,9 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelableList(mFamilies, flags);
- dest.writeParcelableList(mAliases, flags);
+ dest.writeTypedList(mFamilies, flags);
+ dest.writeTypedList(mAliases, flags);
+ dest.writeTypedList(mNamedFamilyLists, flags);
dest.writeLong(mLastModifiedTimeMillis);
dest.writeInt(mConfigVersion);
}
@@ -142,13 +160,15 @@
public static final @NonNull Creator<FontConfig> CREATOR = new Creator<FontConfig>() {
@Override
public FontConfig createFromParcel(Parcel source) {
- List<FontFamily> families = source.readParcelableList(new ArrayList<>(),
- FontFamily.class.getClassLoader(), android.text.FontConfig.FontFamily.class);
- List<Alias> aliases = source.readParcelableList(new ArrayList<>(),
- Alias.class.getClassLoader(), android.text.FontConfig.Alias.class);
+ final List<FontFamily> families = new ArrayList<>();
+ source.readTypedList(families, FontFamily.CREATOR);
+ final List<Alias> aliases = new ArrayList<>();
+ source.readTypedList(aliases, Alias.CREATOR);
+ final List<NamedFamilyList> familyLists = new ArrayList<>();
+ source.readTypedList(familyLists, NamedFamilyList.CREATOR);
long lastModifiedDate = source.readLong();
int configVersion = source.readInt();
- return new FontConfig(families, aliases, lastModifiedDate, configVersion);
+ return new FontConfig(families, aliases, familyLists, lastModifiedDate, configVersion);
}
@Override
@@ -506,7 +526,6 @@
*/
public static final class FontFamily implements Parcelable {
private final @NonNull List<Font> mFonts;
- private final @Nullable String mName;
private final @NonNull LocaleList mLocaleList;
private final @Variant int mVariant;
@@ -547,10 +566,9 @@
*
* @hide Only system server can create this instance and passed via IPC.
*/
- public FontFamily(@NonNull List<Font> fonts, @Nullable String name,
- @NonNull LocaleList localeList, @Variant int variant) {
+ public FontFamily(@NonNull List<Font> fonts, @NonNull LocaleList localeList,
+ @Variant int variant) {
mFonts = fonts;
- mName = name;
mLocaleList = localeList;
mVariant = variant;
}
@@ -577,9 +595,13 @@
*
* When the name of a {@link FontFamily} is null, it will be appended to all of the
* {@code Fallback List}s.
+ *
+ * @deprecated From API 34, this function always returns null. All font families which have
+ * name attribute will be reported as a {@link NamedFamilyList}.
*/
+ @Deprecated
public @Nullable String getName() {
- return mName;
+ return null;
}
/**
@@ -606,8 +628,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelableList(mFonts, flags);
- dest.writeString8(mName);
+ dest.writeTypedList(mFonts, flags);
dest.writeString8(mLocaleList.toLanguageTags());
dest.writeInt(mVariant);
}
@@ -616,13 +637,12 @@
@Override
public FontFamily createFromParcel(Parcel source) {
- List<Font> fonts = source.readParcelableList(
- new ArrayList<>(), Font.class.getClassLoader(), android.text.FontConfig.Font.class);
- String name = source.readString8();
+ List<Font> fonts = new ArrayList<>();
+ source.readTypedList(fonts, Font.CREATOR);
String langTags = source.readString8();
int variant = source.readInt();
- return new FontFamily(fonts, name, LocaleList.forLanguageTags(langTags), variant);
+ return new FontFamily(fonts, LocaleList.forLanguageTags(langTags), variant);
}
@Override
@@ -659,23 +679,118 @@
FontFamily that = (FontFamily) o;
return mVariant == that.mVariant
&& Objects.equals(mFonts, that.mFonts)
- && Objects.equals(mName, that.mName)
&& Objects.equals(mLocaleList, that.mLocaleList);
}
@Override
public int hashCode() {
- return Objects.hash(mFonts, mName, mLocaleList, mVariant);
+ return Objects.hash(mFonts, mLocaleList, mVariant);
}
@Override
public String toString() {
return "FontFamily{"
+ "mFonts=" + mFonts
- + ", mName='" + mName + '\''
+ ", mLocaleList=" + mLocaleList
+ ", mVariant=" + mVariant
+ '}';
}
}
+
+ /**
+ * Represents list of font family in the system font configuration.
+ *
+ * In the fonts_customization.xml, it can define the list of FontFamily as a named family. The
+ * list of FontFamily is treated as a fallback list when drawing.
+ *
+ * @see android.graphics.fonts.FontFamily
+ */
+ public static final class NamedFamilyList implements Parcelable {
+ private final List<FontFamily> mFamilies;
+ private final String mName;
+
+ /** @hide */
+ public NamedFamilyList(@NonNull List<FontFamily> families, @NonNull String name) {
+ mFamilies = families;
+ mName = name;
+ }
+
+ /** @hide */
+ public NamedFamilyList(@NonNull FontFamily family) {
+ mFamilies = new ArrayList<>();
+ mFamilies.add(family);
+ mName = family.getName();
+ }
+
+ /**
+ * A list of font families.
+ *
+ * @return a list of font families.
+ */
+ public @NonNull List<FontFamily> getFamilies() {
+ return mFamilies;
+ }
+
+ /**
+ * Returns the name of the {@link FontFamily}.
+ *
+ * This name is used to create a new {@code Fallback List}.
+ *
+ * For example, if the {@link FontFamily} has the name "serif", then the system will create
+ * a “serif” {@code Fallback List} and it can be used by creating a Typeface via
+ * {@code Typeface.create("serif", Typeface.NORMAL);}
+ */
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mFamilies, flags);
+ dest.writeString8(mName);
+ }
+
+ public static final @NonNull Creator<NamedFamilyList> CREATOR = new Creator<>() {
+
+ @Override
+ public NamedFamilyList createFromParcel(Parcel source) {
+ final List<FontFamily> families = new ArrayList<>();
+ source.readTypedList(families, FontFamily.CREATOR);
+ String name = source.readString8();
+ return new NamedFamilyList(families, name);
+ }
+
+ @Override
+ public NamedFamilyList[] newArray(int size) {
+ return new NamedFamilyList[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NamedFamilyList that = (NamedFamilyList) o;
+ return Objects.equals(mFamilies, that.mFamilies) && Objects.equals(mName,
+ that.mName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFamilies, mName);
+ }
+
+ @Override
+ public String toString() {
+ return "NamedFamilyList{"
+ + "mFamilies=" + mFamilies
+ + ", mName='" + mName + '\''
+ + '}';
+ }
+ }
}
diff --git a/core/tests/coretests/assets/fonts/a3em.ttf b/core/tests/coretests/assets/fonts/a3em.ttf
index a601ce2..4c1ad1b 100644
--- a/core/tests/coretests/assets/fonts/a3em.ttf
+++ b/core/tests/coretests/assets/fonts/a3em.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/a3em.ttx b/core/tests/coretests/assets/fonts/a3em.ttx
index d3b9e16..d7da0da 100644
--- a/core/tests/coretests/assets/fonts/a3em.ttx
+++ b/core/tests/coretests/assets/fonts/a3em.ttx
@@ -156,7 +156,7 @@
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
- SampleFont-Regular
+ a3em
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/core/tests/coretests/assets/fonts/b3em.ttf b/core/tests/coretests/assets/fonts/b3em.ttf
index 63948a2..b18d4bb 100644
--- a/core/tests/coretests/assets/fonts/b3em.ttf
+++ b/core/tests/coretests/assets/fonts/b3em.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/b3em.ttx b/core/tests/coretests/assets/fonts/b3em.ttx
index b5a77ef..217393a 100644
--- a/core/tests/coretests/assets/fonts/b3em.ttx
+++ b/core/tests/coretests/assets/fonts/b3em.ttx
@@ -156,7 +156,7 @@
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
- SampleFont-Regular
+ b3em
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/core/tests/coretests/assets/fonts/c3em.ttf b/core/tests/coretests/assets/fonts/c3em.ttf
index badc3e2..83a5db2 100644
--- a/core/tests/coretests/assets/fonts/c3em.ttf
+++ b/core/tests/coretests/assets/fonts/c3em.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/c3em.ttx b/core/tests/coretests/assets/fonts/c3em.ttx
index f5ed8e5..7535bea 100644
--- a/core/tests/coretests/assets/fonts/c3em.ttx
+++ b/core/tests/coretests/assets/fonts/c3em.ttx
@@ -156,7 +156,7 @@
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
- SampleFont-Regular
+ c3em
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/core/tests/coretests/assets/fonts/fallback.ttf b/core/tests/coretests/assets/fonts/fallback.ttf
new file mode 100644
index 0000000..1ba8639
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/fallback.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/fallback.ttx b/core/tests/coretests/assets/fonts/fallback.ttx
new file mode 100644
index 0000000..8e4b3e6
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/fallback.ttx
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- "a" -->
+ <map code="0x0062" name="1em" /> <!-- "b" -->
+ <map code="0x0063" name="1em" /> <!-- "c" -->
+ <map code="0x0064" name="1em" /> <!-- "d" -->
+ <map code="0x0065" name="1em" /> <!-- "e" -->
+ <map code="0x0066" name="1em" /> <!-- "f" -->
+ <map code="0x0067" name="1em" /> <!-- "g" -->
+ <map code="0x0068" name="1em" /> <!-- "h" -->
+ <map code="0x0069" name="1em" /> <!-- "i" -->
+ <map code="0x006a" name="1em" /> <!-- "j" -->
+ <map code="0x006b" name="1em" /> <!-- "k" -->
+ <map code="0x006c" name="1em" /> <!-- "l" -->
+ <map code="0x006d" name="1em" /> <!-- "m" -->
+ <map code="0x006e" name="1em" /> <!-- "n" -->
+ <map code="0x006f" name="1em" /> <!-- "o" -->
+ <map code="0x0070" name="1em" /> <!-- "p" -->
+ <map code="0x0071" name="1em" /> <!-- "q" -->
+ <map code="0x0072" name="1em" /> <!-- "r" -->
+ <map code="0x0073" name="1em" /> <!-- "s" -->
+ <map code="0x0074" name="1em" /> <!-- "t" -->
+ <map code="0x0075" name="1em" /> <!-- "u" -->
+ <map code="0x0076" name="1em" /> <!-- "v" -->
+ <map code="0x0077" name="1em" /> <!-- "w" -->
+ <map code="0x0078" name="1em" /> <!-- "x" -->
+ <map code="0x0079" name="1em" /> <!-- "y" -->
+ <map code="0x007a" name="1em" /> <!-- "z" -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2017 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ fallback
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts/fallback_capital.ttf b/core/tests/coretests/assets/fonts/fallback_capital.ttf
new file mode 100644
index 0000000..073761f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/fallback_capital.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/fallback_capital.ttx b/core/tests/coretests/assets/fonts/fallback_capital.ttx
new file mode 100644
index 0000000..710c95e
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/fallback_capital.ttx
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0041" name="1em" /> <!-- "A" -->
+ <map code="0x0042" name="1em" /> <!-- "B" -->
+ <map code="0x0043" name="1em" /> <!-- "C" -->
+ <map code="0x0044" name="1em" /> <!-- "D" -->
+ <map code="0x0045" name="1em" /> <!-- "E" -->
+ <map code="0x0046" name="1em" /> <!-- "F" -->
+ <map code="0x0047" name="1em" /> <!-- "G" -->
+ <map code="0x0048" name="1em" /> <!-- "H" -->
+ <map code="0x0049" name="1em" /> <!-- "I" -->
+ <map code="0x004a" name="1em" /> <!-- "J" -->
+ <map code="0x004b" name="1em" /> <!-- "K" -->
+ <map code="0x004c" name="1em" /> <!-- "L" -->
+ <map code="0x004d" name="1em" /> <!-- "M" -->
+ <map code="0x004e" name="1em" /> <!-- "N" -->
+ <map code="0x004f" name="1em" /> <!-- "O" -->
+ <map code="0x0050" name="1em" /> <!-- "P" -->
+ <map code="0x0051" name="1em" /> <!-- "Q" -->
+ <map code="0x0052" name="1em" /> <!-- "R" -->
+ <map code="0x0053" name="1em" /> <!-- "S" -->
+ <map code="0x0054" name="1em" /> <!-- "T" -->
+ <map code="0x0055" name="1em" /> <!-- "U" -->
+ <map code="0x0056" name="1em" /> <!-- "V" -->
+ <map code="0x0057" name="1em" /> <!-- "W" -->
+ <map code="0x0058" name="1em" /> <!-- "X" -->
+ <map code="0x0059" name="1em" /> <!-- "Y" -->
+ <map code="0x005a" name="1em" /> <!-- "Z" -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2017 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ fallback_capital
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index 479e52a..d46f762 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -47,6 +47,7 @@
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
@SmallTest
@@ -59,14 +60,12 @@
+ "<family name='sans-serif'>"
+ " <font>test.ttf</font>"
+ "</family>";
- FontConfig.FontFamily expected = new FontConfig.FontFamily(
- Arrays.asList(
- new FontConfig.Font(new File("test.ttf"), null, "test",
- new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
- 0, "", null)),
- "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
-
- FontConfig.FontFamily family = readFamily(xml);
+ FontConfig.NamedFamilyList expected = new FontConfig.NamedFamilyList(
+ Collections.singletonList(new FontConfig.FontFamily(
+ Arrays.asList(new FontConfig.Font(new File("test.ttf"), null, "test",
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", null)),
+ LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), "sans-serif");
+ FontConfig.NamedFamilyList family = readNamedFamily(xml);
assertThat(family).isEqualTo(expected);
}
@@ -85,7 +84,7 @@
new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", "serif")),
- null, LocaleList.forLanguageTags("en"), VARIANT_DEFAULT);
+ LocaleList.forLanguageTags("en"), VARIANT_DEFAULT);
FontConfig.FontFamily family = readFamily(xml);
assertThat(family).isEqualTo(expected);
@@ -102,7 +101,7 @@
new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null)),
- null, LocaleList.forLanguageTags("en"), VARIANT_COMPACT);
+ LocaleList.forLanguageTags("en"), VARIANT_COMPACT);
FontConfig.FontFamily family = readFamily(xml);
assertThat(family).isEqualTo(expected);
@@ -119,7 +118,7 @@
new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null)),
- null, LocaleList.forLanguageTags("en"), VARIANT_ELEGANT);
+ LocaleList.forLanguageTags("en"), VARIANT_ELEGANT);
FontConfig.FontFamily family = readFamily(xml);
assertThat(family).isEqualTo(expected);
@@ -133,19 +132,16 @@
+ " <font weight='100'>weight.ttf</font>"
+ " <font style='italic'>italic.ttf</font>"
+ "</family>";
- FontConfig.FontFamily expected = new FontConfig.FontFamily(
- Arrays.asList(
- new FontConfig.Font(new File("normal.ttf"), null, "test",
- new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
- 0, "", null),
- new FontConfig.Font(new File("weight.ttf"), null, "test",
- new FontStyle(100, FONT_SLANT_UPRIGHT),
- 0, "", null),
- new FontConfig.Font(new File("italic.ttf"), null, "test",
- new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC),
- 0, "", null)),
- "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
- FontConfig.FontFamily family = readFamily(xml);
+ FontConfig.NamedFamilyList expected = new FontConfig.NamedFamilyList(
+ Collections.singletonList(new FontConfig.FontFamily(Arrays.asList(
+ new FontConfig.Font(new File("normal.ttf"), null, "test",
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", null),
+ new FontConfig.Font(new File("weight.ttf"), null, "test",
+ new FontStyle(100, FONT_SLANT_UPRIGHT), 0, "", null),
+ new FontConfig.Font(new File("italic.ttf"), null, "test",
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC), 0, "", null)),
+ LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), "sans-serif");
+ FontConfig.NamedFamilyList family = readNamedFamily(xml);
assertThat(family).isEqualTo(expected);
}
@@ -162,16 +158,17 @@
+ " <axis tag='wght' stylevalue='700' />"
+ " </font>"
+ "</family>";
- FontConfig.FontFamily expected = new FontConfig.FontFamily(
- Arrays.asList(
+ FontConfig.NamedFamilyList expected = new FontConfig.NamedFamilyList(
+ Collections.singletonList(new FontConfig.FontFamily(Arrays.asList(
new FontConfig.Font(new File("test-VF.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "'wdth' 100.0,'wght' 200.0", null),
new FontConfig.Font(new File("test-VF.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "'wdth' 400.0,'wght' 700.0", null)),
- "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
- FontConfig.FontFamily family = readFamily(xml);
+ LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)),
+ "sans-serif");
+ FontConfig.NamedFamilyList family = readNamedFamily(xml);
assertThat(family).isEqualTo(expected);
}
@@ -182,16 +179,17 @@
+ " <font index='0'>test.ttc</font>"
+ " <font index='1'>test.ttc</font>"
+ "</family>";
- FontConfig.FontFamily expected = new FontConfig.FontFamily(
- Arrays.asList(
+ FontConfig.NamedFamilyList expected = new FontConfig.NamedFamilyList(
+ Collections.singletonList(new FontConfig.FontFamily(Arrays.asList(
new FontConfig.Font(new File("test.ttc"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null),
new FontConfig.Font(new File("test.ttc"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
1, "", null)),
- "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
- FontConfig.FontFamily family = readFamily(xml);
+ LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)),
+ "sans-serif");
+ FontConfig.NamedFamilyList family = readNamedFamily(xml);
assertThat(family).isEqualTo(expected);
}
@@ -202,16 +200,14 @@
+ " <font index='0' postScriptName='foo'>test.ttc</font>"
+ " <font index='1'>test.ttc</font>"
+ "</family>";
- FontConfig.FontFamily expected = new FontConfig.FontFamily(
- Arrays.asList(
- new FontConfig.Font(new File("test.ttc"), null, "foo",
- new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
- 0, "", null),
- new FontConfig.Font(new File("test.ttc"), null, "test",
- new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
- 1, "", null)),
- "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
- FontConfig.FontFamily family = readFamily(xml);
+ FontConfig.NamedFamilyList expected = new FontConfig.NamedFamilyList(
+ Collections.singletonList(new FontConfig.FontFamily(Arrays.asList(
+ new FontConfig.Font(new File("test.ttc"), null, "foo",
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", null),
+ new FontConfig.Font(new File("test.ttc"), null, "test",
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 1, "", null)),
+ LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), "sans-serif");
+ FontConfig.NamedFamilyList family = readNamedFamily(xml);
assertThat(family).isEqualTo(expected);
}
@@ -396,4 +392,14 @@
parser.nextTag();
return FontListParser.readFamily(parser, "", null, true);
}
+
+ private FontConfig.NamedFamilyList readNamedFamily(String xml)
+ throws IOException, XmlPullParserException {
+ ByteArrayInputStream buffer = new ByteArrayInputStream(
+ xml.getBytes(StandardCharsets.UTF_8));
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(buffer, "UTF-8");
+ parser.nextTag();
+ return FontListParser.readNamedFamily(parser, "", null, true);
+ }
}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 3df2e90..a8a5059 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -28,6 +28,8 @@
import android.graphics.fonts.FontCustomizationParser;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
+import android.graphics.text.PositionedGlyphs;
+import android.graphics.text.TextRunShaper;
import android.text.FontConfig;
import android.util.ArrayMap;
@@ -64,6 +66,8 @@
"b3em.ttf", // Supports "a","b","c". The width of "b" is 3em, others are 1em.
"c3em.ttf", // Supports "a","b","c". The width of "c" is 3em, others are 1em.
"all2em.ttf", // Supports "a,","b","c". All of them have the same width of 2em.
+ "fallback.ttf", // SUpports all small alphabets.
+ "fallback_capital.ttf", // SUpports all capital alphabets.
"no_coverage.ttf", // This font doesn't support any characters.
};
private static final String TEST_FONTS_XML;
@@ -165,7 +169,10 @@
Map<String, File> updatableFontMap = new HashMap<>();
for (File file : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) {
- updatableFontMap.put(file.getName(), file);
+ final String fileName = file.getName();
+ final int periodIndex = fileName.lastIndexOf(".");
+ final String psName = fileName.substring(0, periodIndex);
+ updatableFontMap.put(psName, file);
}
FontConfig fontConfig;
@@ -710,6 +717,47 @@
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
+ private String getFontName(Paint paint, String text) {
+ PositionedGlyphs glyphs = TextRunShaper.shapeTextRun(
+ text, 0, text.length(), 0, text.length(), 0f, 0f, false, paint);
+ assertEquals(1, glyphs.glyphCount());
+ return glyphs.getFont(0).getFile().getName();
+ }
+
+ @Test
+ public void testBuildSystemFallback__Customization_new_named_familyList() {
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font weight='400' style='normal'>fallback_capital.ttf</font>"
+ + " </family>"
+ + "</familyset>";
+ final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family-list customizationType='new-named-family' name='google-sans'>"
+ + " <family>"
+ + " <font weight='400' style='normal'>b3em.ttf</font>"
+ + " </family>"
+ + " <family>"
+ + " <font weight='400' style='normal'>fallback.ttf</font>"
+ + " </family>"
+ + " </family-list>"
+ + "</fonts-modification>";
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+ buildSystemFallback(xml, oemXml, fontMap, fallbackMap);
+
+ final Paint paint = new Paint();
+
+ Typeface testTypeface = fontMap.get("google-sans");
+ assertNotNull(testTypeface);
+ paint.setTypeface(testTypeface);
+ assertEquals("b3em.ttf", getFontName(paint, "a"));
+ assertEquals("fallback.ttf", getFontName(paint, "x"));
+ assertEquals("fallback_capital.ttf", getFontName(paint, "A"));
+ }
+
@Test
public void testBuildSystemFallback__Customization_new_named_family_override() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 4bb16c6..674246a 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,6 +16,8 @@
package android.graphics;
+import static android.text.FontConfig.NamedFamilyList;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -36,6 +38,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -46,6 +49,7 @@
* @hide
*/
public class FontListParser {
+ private static final String TAG = "FontListParser";
// XML constants for FontFamily.
private static final String ATTR_NAME = "name";
@@ -148,27 +152,60 @@
boolean allowNonExistingFile)
throws XmlPullParserException, IOException {
List<FontConfig.FontFamily> families = new ArrayList<>();
+ List<FontConfig.NamedFamilyList> resultNamedFamilies = new ArrayList<>();
List<FontConfig.Alias> aliases = new ArrayList<>(customization.getAdditionalAliases());
- Map<String, FontConfig.FontFamily> oemNamedFamilies =
+ Map<String, NamedFamilyList> oemNamedFamilies =
customization.getAdditionalNamedFamilies();
+ boolean firstFamily = true;
parser.require(XmlPullParser.START_TAG, null, "familyset");
while (keepReading(parser)) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("family")) {
- FontConfig.FontFamily family = readFamily(parser, fontDir, updatableFontMap,
- allowNonExistingFile);
- if (family == null) {
+ final String name = parser.getAttributeValue(null, "name");
+ if (name == null) {
+ FontConfig.FontFamily family = readFamily(parser, fontDir, updatableFontMap,
+ allowNonExistingFile);
+ if (family == null) {
+ continue;
+ }
+ families.add(family);
+
+ } else {
+ FontConfig.NamedFamilyList namedFamilyList = readNamedFamily(
+ parser, fontDir, updatableFontMap, allowNonExistingFile);
+ if (namedFamilyList == null) {
+ continue;
+ }
+ if (!oemNamedFamilies.containsKey(name)) {
+ // The OEM customization overrides system named family. Skip if OEM
+ // customization XML defines the same named family.
+ resultNamedFamilies.add(namedFamilyList);
+ }
+ if (firstFamily) {
+ // The first font family is used as a fallback family as well.
+ families.addAll(namedFamilyList.getFamilies());
+ }
+ }
+ firstFamily = false;
+ } else if (tag.equals("family-list")) {
+ FontConfig.NamedFamilyList namedFamilyList = readNamedFamilyList(
+ parser, fontDir, updatableFontMap, allowNonExistingFile);
+ if (namedFamilyList == null) {
continue;
}
- String name = family.getName();
- if (name == null || !oemNamedFamilies.containsKey(name)) {
+ if (!oemNamedFamilies.containsKey(namedFamilyList.getName())) {
// The OEM customization overrides system named family. Skip if OEM
// customization XML defines the same named family.
- families.add(family);
+ resultNamedFamilies.add(namedFamilyList);
}
+ if (firstFamily) {
+ // The first font family is used as a fallback family as well.
+ families.addAll(namedFamilyList.getFamilies());
+ }
+ firstFamily = false;
} else if (tag.equals("alias")) {
aliases.add(readAlias(parser));
} else {
@@ -176,12 +213,12 @@
}
}
- families.addAll(oemNamedFamilies.values());
+ resultNamedFamilies.addAll(oemNamedFamilies.values());
// Filters aliases that point to non-existing families.
Set<String> namedFamilies = new ArraySet<>();
- for (int i = 0; i < families.size(); ++i) {
- String name = families.get(i).getName();
+ for (int i = 0; i < resultNamedFamilies.size(); ++i) {
+ String name = resultNamedFamilies.get(i).getName();
if (name != null) {
namedFamilies.add(name);
}
@@ -194,7 +231,8 @@
}
}
- return new FontConfig(families, filtered, lastModifiedDate, configVersion);
+ return new FontConfig(families, filtered, resultNamedFamilies, lastModifiedDate,
+ configVersion);
}
private static boolean keepReading(XmlPullParser parser)
@@ -215,7 +253,6 @@
public static @Nullable FontConfig.FontFamily readFamily(XmlPullParser parser, String fontDir,
@Nullable Map<String, File> updatableFontMap, boolean allowNonExistingFile)
throws XmlPullParserException, IOException {
- final String name = parser.getAttributeValue(null, "name");
final String lang = parser.getAttributeValue("", "lang");
final String variant = parser.getAttributeValue(null, "variant");
final String ignore = parser.getAttributeValue(null, "ignore");
@@ -246,7 +283,68 @@
if (skip || fonts.isEmpty()) {
return null;
}
- return new FontConfig.FontFamily(fonts, name, LocaleList.forLanguageTags(lang), intVariant);
+ return new FontConfig.FontFamily(fonts, LocaleList.forLanguageTags(lang), intVariant);
+ }
+
+ private static void throwIfAttributeExists(String attrName, XmlPullParser parser) {
+ if (parser.getAttributeValue(null, attrName) != null) {
+ throw new IllegalArgumentException(attrName + " cannot be used in FontFamily inside "
+ + " family or family-list with name attribute.");
+ }
+ }
+
+ /**
+ * Read a font family with name attribute as a single element family-list element.
+ */
+ public static @Nullable FontConfig.NamedFamilyList readNamedFamily(
+ @NonNull XmlPullParser parser, @NonNull String fontDir,
+ @Nullable Map<String, File> updatableFontMap, boolean allowNonExistingFile)
+ throws XmlPullParserException, IOException {
+ final String name = parser.getAttributeValue(null, "name");
+ throwIfAttributeExists("lang", parser);
+ throwIfAttributeExists("variant", parser);
+ throwIfAttributeExists("ignore", parser);
+
+ final FontConfig.FontFamily family = readFamily(parser, fontDir, updatableFontMap,
+ allowNonExistingFile);
+ if (family == null) {
+ return null;
+ }
+ return new NamedFamilyList(Collections.singletonList(family), name);
+ }
+
+ /**
+ * Read a family-list element
+ */
+ public static @Nullable FontConfig.NamedFamilyList readNamedFamilyList(
+ @NonNull XmlPullParser parser, @NonNull String fontDir,
+ @Nullable Map<String, File> updatableFontMap, boolean allowNonExistingFile)
+ throws XmlPullParserException, IOException {
+ final String name = parser.getAttributeValue(null, "name");
+ final List<FontConfig.FontFamily> familyList = new ArrayList<>();
+ while (keepReading(parser)) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+ final String tag = parser.getName();
+ if (tag.equals("family")) {
+ throwIfAttributeExists("name", parser);
+ throwIfAttributeExists("lang", parser);
+ throwIfAttributeExists("variant", parser);
+ throwIfAttributeExists("ignore", parser);
+
+ final FontConfig.FontFamily family = readFamily(parser, fontDir, updatableFontMap,
+ allowNonExistingFile);
+ if (family != null) {
+ familyList.add(family);
+ }
+ } else {
+ skip(parser);
+ }
+ }
+
+ if (familyList.isEmpty()) {
+ return null;
+ }
+ return new FontConfig.NamedFamilyList(familyList, name);
}
/** Matches leading and trailing XML whitespace. */
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index df47f73..b458dd9 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -17,7 +17,7 @@
package android.graphics.fonts;
import static android.text.FontConfig.Alias;
-import static android.text.FontConfig.FontFamily;
+import static android.text.FontConfig.NamedFamilyList;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,11 +42,14 @@
* @hide
*/
public class FontCustomizationParser {
+ private static final String TAG = "FontCustomizationParser";
+
/**
* Represents a customization XML
*/
public static class Result {
- private final Map<String, FontFamily> mAdditionalNamedFamilies;
+ private final Map<String, NamedFamilyList> mAdditionalNamedFamilies;
+
private final List<Alias> mAdditionalAliases;
public Result() {
@@ -54,13 +57,13 @@
mAdditionalAliases = Collections.emptyList();
}
- public Result(Map<String, FontFamily> additionalNamedFamilies,
+ public Result(Map<String, NamedFamilyList> additionalNamedFamilies,
List<Alias> additionalAliases) {
mAdditionalNamedFamilies = additionalNamedFamilies;
mAdditionalAliases = additionalAliases;
}
- public Map<String, FontFamily> getAdditionalNamedFamilies() {
+ public Map<String, NamedFamilyList> getAdditionalNamedFamilies() {
return mAdditionalNamedFamilies;
}
@@ -85,20 +88,24 @@
return readFamilies(parser, fontDir, updatableFontMap);
}
- private static Map<String, FontFamily> validateAndTransformToMap(List<FontFamily> families) {
- HashMap<String, FontFamily> namedFamily = new HashMap<>();
+ private static Result validateAndTransformToResult(
+ List<NamedFamilyList> families, List<Alias> aliases) {
+ HashMap<String, NamedFamilyList> namedFamily = new HashMap<>();
for (int i = 0; i < families.size(); ++i) {
- final FontFamily family = families.get(i);
+ final NamedFamilyList family = families.get(i);
final String name = family.getName();
- if (name == null) {
- throw new IllegalArgumentException("new-named-family requires name attribute");
- }
- if (namedFamily.put(name, family) != null) {
+ if (name != null) {
+ if (namedFamily.put(name, family) != null) {
+ throw new IllegalArgumentException(
+ "new-named-family requires unique name attribute");
+ }
+ } else {
throw new IllegalArgumentException(
- "new-named-family requires unique name attribute");
+ "new-named-family requires name attribute or new-default-fallback-family"
+ + "requires fallackTarget attribute");
}
}
- return namedFamily;
+ return new Result(namedFamily, aliases);
}
private static Result readFamilies(
@@ -106,7 +113,7 @@
@NonNull String fontDir,
@Nullable Map<String, File> updatableFontMap
) throws XmlPullParserException, IOException {
- List<FontFamily> families = new ArrayList<>();
+ List<NamedFamilyList> families = new ArrayList<>();
List<Alias> aliases = new ArrayList<>();
parser.require(XmlPullParser.START_TAG, null, "fonts-modification");
while (parser.next() != XmlPullParser.END_TAG) {
@@ -114,19 +121,21 @@
String tag = parser.getName();
if (tag.equals("family")) {
readFamily(parser, fontDir, families, updatableFontMap);
+ } else if (tag.equals("family-list")) {
+ readFamilyList(parser, fontDir, families, updatableFontMap);
} else if (tag.equals("alias")) {
aliases.add(FontListParser.readAlias(parser));
} else {
FontListParser.skip(parser);
}
}
- return new Result(validateAndTransformToMap(families), aliases);
+ return validateAndTransformToResult(families, aliases);
}
private static void readFamily(
@NonNull XmlPullParser parser,
@NonNull String fontDir,
- @NonNull List<FontFamily> out,
+ @NonNull List<NamedFamilyList> out,
@Nullable Map<String, File> updatableFontMap)
throws XmlPullParserException, IOException {
final String customizationType = parser.getAttributeValue(null, "customizationType");
@@ -134,7 +143,28 @@
throw new IllegalArgumentException("customizationType must be specified");
}
if (customizationType.equals("new-named-family")) {
- FontFamily fontFamily = FontListParser.readFamily(
+ NamedFamilyList fontFamily = FontListParser.readNamedFamily(
+ parser, fontDir, updatableFontMap, false);
+ if (fontFamily != null) {
+ out.add(fontFamily);
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
+ }
+ }
+
+ private static void readFamilyList(
+ @NonNull XmlPullParser parser,
+ @NonNull String fontDir,
+ @NonNull List<NamedFamilyList> 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")) {
+ NamedFamilyList fontFamily = FontListParser.readNamedFamilyList(
parser, fontDir, updatableFontMap, false);
if (fontFamily != null) {
out.add(fontFamily);
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index a771a6e..bf79b1b 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -114,17 +114,19 @@
* @return a font family
*/
public @NonNull FontFamily build() {
- return build("", FontConfig.FontFamily.VARIANT_DEFAULT, true /* isCustomFallback */);
+ return build("", FontConfig.FontFamily.VARIANT_DEFAULT, true /* isCustomFallback */,
+ false /* isDefaultFallback */);
}
/** @hide */
public @NonNull FontFamily build(@NonNull String langTags, int variant,
- boolean isCustomFallback) {
+ boolean isCustomFallback, boolean isDefaultFallback) {
final long builderPtr = nInitBuilder();
for (int i = 0; i < mFonts.size(); ++i) {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
- final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
+ final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback,
+ isDefaultFallback);
final FontFamily family = new FontFamily(ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
@@ -138,7 +140,7 @@
@CriticalNative
private static native void nAddFont(long builderPtr, long fontPtr);
private static native long nBuild(long builderPtr, String langTags, int variant,
- boolean isCustomFallback);
+ boolean isCustomFallback, boolean isDefaultFallback);
@CriticalNative
private static native long nGetReleaseNativeFamily();
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 6278c0e..ec8b2d6 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -22,6 +22,7 @@
import android.graphics.Typeface;
import android.text.FontConfig;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -92,9 +93,8 @@
}
private static void pushFamilyToFallback(@NonNull FontConfig.FontFamily xmlFamily,
- @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
+ @NonNull ArrayMap<String, NativeFamilyListSet> fallbackMap,
@NonNull Map<String, ByteBuffer> cache) {
-
final String languageTags = xmlFamily.getLocaleList().toLanguageTags();
final int variant = xmlFamily.getVariant();
@@ -118,26 +118,29 @@
}
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
- xmlFamily.getName(), defaultFonts, languageTags, variant, cache);
+ defaultFonts, languageTags, variant, false, cache);
// Insert family into fallback map.
for (int i = 0; i < fallbackMap.size(); i++) {
- String name = fallbackMap.keyAt(i);
+ final String name = fallbackMap.keyAt(i);
+ final NativeFamilyListSet familyListSet = fallbackMap.valueAt(i);
+ if (familyListSet.seenXmlFamilies.contains(xmlFamily)) {
+ continue;
+ } else {
+ familyListSet.seenXmlFamilies.add(xmlFamily);
+ }
final ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(name);
if (fallback == null) {
- String familyName = xmlFamily.getName();
- if (defaultFamily != null
- // do not add myself to the fallback chain.
- && (familyName == null || !familyName.equals(name))) {
- fallbackMap.valueAt(i).add(defaultFamily);
+ if (defaultFamily != null) {
+ familyListSet.familyList.add(defaultFamily);
}
} else {
- final FontFamily family = createFontFamily(
- xmlFamily.getName(), fallback, languageTags, variant, cache);
+ final FontFamily family = createFontFamily(fallback, languageTags, variant, false,
+ cache);
if (family != null) {
- fallbackMap.valueAt(i).add(family);
+ familyListSet.familyList.add(family);
} else if (defaultFamily != null) {
- fallbackMap.valueAt(i).add(defaultFamily);
+ familyListSet.familyList.add(defaultFamily);
} else {
// There is no valid for for default fallback. Ignore.
}
@@ -145,10 +148,11 @@
}
}
- private static @Nullable FontFamily createFontFamily(@NonNull String familyName,
+ private static @Nullable FontFamily createFontFamily(
@NonNull List<FontConfig.Font> fonts,
@NonNull String languageTags,
@FontConfig.FontFamily.Variant int variant,
+ boolean isDefaultFallback,
@NonNull Map<String, ByteBuffer> cache) {
if (fonts.size() == 0) {
return null;
@@ -188,23 +192,30 @@
b.addFont(font);
}
}
- return b == null ? null : b.build(languageTags, variant, false /* isCustomFallback */);
+ return b == null ? null : b.build(languageTags, variant, false /* isCustomFallback */,
+ isDefaultFallback);
}
- private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily,
+ private static void appendNamedFamilyList(@NonNull FontConfig.NamedFamilyList namedFamilyList,
@NonNull ArrayMap<String, ByteBuffer> bufferCache,
- @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) {
- final String familyName = xmlFamily.getName();
- final FontFamily family = createFontFamily(
- familyName, xmlFamily.getFontList(),
- xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getVariant(),
- bufferCache);
- if (family == null) {
- return;
+ @NonNull ArrayMap<String, NativeFamilyListSet> fallbackListMap) {
+ final String familyName = namedFamilyList.getName();
+ final NativeFamilyListSet familyListSet = new NativeFamilyListSet();
+ final List<FontConfig.FontFamily> xmlFamilies = namedFamilyList.getFamilies();
+ for (int i = 0; i < xmlFamilies.size(); ++i) {
+ FontConfig.FontFamily xmlFamily = xmlFamilies.get(i);
+ final FontFamily family = createFontFamily(
+ xmlFamily.getFontList(),
+ xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getVariant(),
+ true, // named family is always default
+ bufferCache);
+ if (family == null) {
+ return;
+ }
+ familyListSet.familyList.add(family);
+ familyListSet.seenXmlFamilies.add(xmlFamily);
}
- final ArrayList<FontFamily> fallback = new ArrayList<>();
- fallback.add(family);
- fallbackListMap.put(familyName, fallback);
+ fallbackListMap.put(familyName, familyListSet);
}
/**
@@ -245,10 +256,12 @@
updatableFontMap, lastModifiedDate, configVersion);
} catch (IOException e) {
Log.e(TAG, "Failed to open/read system font configurations.", e);
- return new FontConfig(Collections.emptyList(), Collections.emptyList(), 0, 0);
+ return new FontConfig(Collections.emptyList(), Collections.emptyList(),
+ Collections.emptyList(), 0, 0);
} catch (XmlPullParserException e) {
Log.e(TAG, "Failed to parse the system font configuration.", e);
- return new FontConfig(Collections.emptyList(), Collections.emptyList(), 0, 0);
+ return new FontConfig(Collections.emptyList(), Collections.emptyList(),
+ Collections.emptyList(), 0, 0);
}
}
@@ -261,37 +274,36 @@
return buildSystemFallback(fontConfig, new ArrayMap<>());
}
+ private static final class NativeFamilyListSet {
+ public List<FontFamily> familyList = new ArrayList<>();
+ public Set<FontConfig.FontFamily> seenXmlFamilies = new ArraySet<>();
+ }
+
/** @hide */
@VisibleForTesting
public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig,
ArrayMap<String, ByteBuffer> outBufferCache) {
- final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
- final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
- final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
- // First traverse families which have a 'name' attribute to create fallback map.
- for (final FontConfig.FontFamily xmlFamily : xmlFamilies) {
- final String familyName = xmlFamily.getName();
- if (familyName == null) {
- continue;
- }
- appendNamedFamily(xmlFamily, outBufferCache, fallbackListMap);
+ final ArrayMap<String, NativeFamilyListSet> fallbackListMap = new ArrayMap<>();
+
+ final List<FontConfig.NamedFamilyList> namedFamilies = fontConfig.getNamedFamilyLists();
+ for (int i = 0; i < namedFamilies.size(); ++i) {
+ FontConfig.NamedFamilyList namedFamilyList = namedFamilies.get(i);
+ appendNamedFamilyList(namedFamilyList, outBufferCache, fallbackListMap);
}
- // Then, add fallback fonts to the each fallback map.
+ // Then, add fallback fonts to the fallback map.
+ final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
for (int i = 0; i < xmlFamilies.size(); i++) {
final FontConfig.FontFamily 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.getName() == null) {
- pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
- }
+ pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
}
// Build the font map and fallback map.
+ final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
for (int i = 0; i < fallbackListMap.size(); i++) {
final String fallbackName = fallbackListMap.keyAt(i);
- final List<FontFamily> familyList = fallbackListMap.valueAt(i);
+ final List<FontFamily> familyList = fallbackListMap.valueAt(i).familyList;
fallbackMap.put(fallbackName, familyList.toArray(new FontFamily[0]));
}
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index c146ada..28e71d7 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -85,9 +85,9 @@
if (builder->fonts.empty()) {
return 0;
}
- std::shared_ptr<minikin::FontFamily> family =
- minikin::FontFamily::create(builder->langId, builder->variant,
- std::move(builder->fonts), true /* isCustomFallback */);
+ std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
+ builder->langId, builder->variant, std::move(builder->fonts),
+ true /* isCustomFallback */, false /* isDefaultFallback */);
if (family->getCoverage().length() == 0) {
return 0;
}
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index fbfc07e..897c4d7 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -57,7 +57,8 @@
// Regular JNI
static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr,
- jstring langTags, jint variant, jboolean isCustomFallback) {
+ jstring langTags, jint variant, jboolean isCustomFallback,
+ jboolean isDefaultFallback) {
std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
uint32_t localeId;
if (langTags == nullptr) {
@@ -66,9 +67,9 @@
ScopedUtfChars str(env, langTags);
localeId = minikin::registerLocaleList(str.c_str());
}
- std::shared_ptr<minikin::FontFamily> family =
- minikin::FontFamily::create(localeId, static_cast<minikin::FamilyVariant>(variant),
- std::move(builder->fonts), isCustomFallback);
+ std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
+ localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts),
+ isCustomFallback, isDefaultFallback);
if (family->getCoverage().length() == 0) {
// No coverage means minikin rejected given font for some reasons.
jniThrowException(env, "java/lang/IllegalArgumentException",
@@ -116,10 +117,10 @@
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
- { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
- { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
- { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build },
- { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
+ {"nInitBuilder", "()J", (void*)FontFamily_Builder_initBuilder},
+ {"nAddFont", "(JJ)V", (void*)FontFamily_Builder_addFont},
+ {"nBuild", "(JLjava/lang/String;IZZ)J", (void*)FontFamily_Builder_build},
+ {"nGetReleaseNativeFamily", "()J", (void*)FontFamily_Builder_GetReleaseFunc},
};
static const JNINativeMethod gFontFamilyMethods[] = {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index 4cd0d6e..2ac2833 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -163,19 +163,25 @@
// Dump named font family first.
List<FontConfig.FontFamily> families = fontConfig.getFontFamilies();
- w.println("Named Font Families");
+ // Dump FontFamilyList
+ w.println("Named Family List");
w.increaseIndent();
- for (int i = 0; i < families.size(); ++i) {
- final FontConfig.FontFamily family = families.get(i);
-
- // Here, only dump the named family only.
- if (family.getName() == null) continue;
-
- w.println("Named Family (" + family.getName() + ")");
- final List<FontConfig.Font> fonts = family.getFontList();
+ List<FontConfig.NamedFamilyList> namedFamilyLists = fontConfig.getNamedFamilyLists();
+ for (int i = 0; i < namedFamilyLists.size(); ++i) {
+ final FontConfig.NamedFamilyList namedFamilyList = namedFamilyLists.get(i);
+ w.println("Named Family (" + namedFamilyList.getName() + ")");
w.increaseIndent();
- for (int j = 0; j < fonts.size(); ++j) {
- dumpSingleFontConfig(w, fonts.get(j));
+ final List<FontConfig.FontFamily> namedFamilies = namedFamilyList.getFamilies();
+ for (int j = 0; j < namedFamilies.size(); ++j) {
+ final FontConfig.FontFamily family = namedFamilies.get(j);
+
+ w.println("Family");
+ final List<FontConfig.Font> fonts = family.getFontList();
+ w.increaseIndent();
+ for (int k = 0; k < fonts.size(); ++k) {
+ dumpSingleFontConfig(w, fonts.get(k));
+ }
+ w.decreaseIndent();
}
w.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 6f93608..a680f50 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -19,7 +19,6 @@
import static com.android.server.graphics.fonts.FontManagerService.SystemFontException;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.SystemFonts;
@@ -44,6 +43,7 @@
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -286,7 +286,7 @@
// Before processing font family update, check all family points the available fonts.
for (FontUpdateRequest.Family family : familyMap.values()) {
- if (resolveFontFiles(family) == null) {
+ if (resolveFontFilesForNamedFamily(family) == null) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FONT_NOT_FOUND,
"Required fonts are not available");
@@ -498,6 +498,19 @@
}
}
}
+ for (int i = 0; i < fontConfig.getNamedFamilyLists().size(); ++i) {
+ FontConfig.NamedFamilyList namedFamilyList = fontConfig.getNamedFamilyLists().get(i);
+ for (int j = 0; j < namedFamilyList.getFamilies().size(); ++j) {
+ FontConfig.FontFamily family = namedFamilyList.getFamilies().get(j);
+ for (int k = 0; k < family.getFontList().size(); ++k) {
+ FontConfig.Font font = family.getFontList().get(k);
+ if (font.getPostScriptName().equals(psName)) {
+ targetFont = font;
+ break;
+ }
+ }
+ }
+ }
if (targetFont == null) {
return -1;
}
@@ -553,8 +566,8 @@
}
}
- @Nullable
- private FontConfig.FontFamily resolveFontFiles(FontUpdateRequest.Family fontFamily) {
+ private FontConfig.NamedFamilyList resolveFontFilesForNamedFamily(
+ FontUpdateRequest.Family fontFamily) {
List<FontUpdateRequest.Font> fontList = fontFamily.getFonts();
List<FontConfig.Font> resolvedFonts = new ArrayList<>(fontList.size());
for (int i = 0; i < fontList.size(); i++) {
@@ -567,8 +580,10 @@
resolvedFonts.add(new FontConfig.Font(info.mFile, null, info.getPostScriptName(),
font.getFontStyle(), font.getIndex(), font.getFontVariationSettings(), null));
}
- return new FontConfig.FontFamily(resolvedFonts, fontFamily.getName(),
+ FontConfig.FontFamily family = new FontConfig.FontFamily(resolvedFonts,
LocaleList.getEmptyLocaleList(), FontConfig.FontFamily.VARIANT_DEFAULT);
+ return new FontConfig.NamedFamilyList(Collections.singletonList(family),
+ fontFamily.getName());
}
Map<String, File> getPostScriptMap() {
@@ -585,23 +600,24 @@
PersistentSystemFontConfig.Config persistentConfig = readPersistentConfig();
List<FontUpdateRequest.Family> families = persistentConfig.fontFamilies;
- List<FontConfig.FontFamily> mergedFamilies =
- new ArrayList<>(config.getFontFamilies().size() + families.size());
+ List<FontConfig.NamedFamilyList> mergedFamilies =
+ new ArrayList<>(config.getNamedFamilyLists().size() + families.size());
// We should keep the first font family (config.getFontFamilies().get(0)) because it's used
// as a fallback font. See SystemFonts.java.
- mergedFamilies.addAll(config.getFontFamilies());
+ mergedFamilies.addAll(config.getNamedFamilyLists());
// When building Typeface, a latter font family definition will override the previous font
// family definition with the same name. An exception is config.getFontFamilies.get(0),
// which will be used as a fallback font without being overridden.
for (int i = 0; i < families.size(); ++i) {
- FontConfig.FontFamily family = resolveFontFiles(families.get(i));
+ FontConfig.NamedFamilyList family = resolveFontFilesForNamedFamily(families.get(i));
if (family != null) {
mergedFamilies.add(family);
}
}
return new FontConfig(
- mergedFamilies, config.getAliases(), mLastModifiedMillis, mConfigVersion);
+ config.getFontFamilies(), config.getAliases(), mergedFamilies, mLastModifiedMillis,
+ mConfigVersion);
}
private PersistentSystemFontConfig.Config readPersistentConfig() {
@@ -635,12 +651,12 @@
return mConfigVersion;
}
- public Map<String, FontConfig.FontFamily> getFontFamilyMap() {
+ public Map<String, FontConfig.NamedFamilyList> getFontFamilyMap() {
PersistentSystemFontConfig.Config curConfig = readPersistentConfig();
- Map<String, FontConfig.FontFamily> familyMap = new HashMap<>();
+ Map<String, FontConfig.NamedFamilyList> familyMap = new HashMap<>();
for (int i = 0; i < curConfig.fontFamilies.size(); ++i) {
FontUpdateRequest.Family family = curConfig.fontFamilies.get(i);
- FontConfig.FontFamily resolvedFamily = resolveFontFiles(family);
+ FontConfig.NamedFamilyList resolvedFamily = resolveFontFilesForNamedFamily(family);
if (resolvedFamily != null) {
familyMap.put(family.getName(), resolvedFamily);
}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 68e5ebf..e9a7d85 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -210,7 +210,8 @@
assertThat(mUpdatableFontFilesDir.list()).hasLength(2);
assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar");
assertThat(dir.getFontFamilyMap()).containsKey("foobar");
- FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar");
+ assertThat(dir.getFontFamilyMap().get("foobar").getFamilies().size()).isEqualTo(1);
+ FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar").getFamilies().get(0);
assertThat(foobar.getFontList()).hasSize(2);
assertThat(foobar.getFontList().get(0).getFile())
.isEqualTo(dir.getPostScriptMap().get("foo"));
@@ -326,10 +327,12 @@
new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, null);
FontConfig.FontFamily family = new FontConfig.FontFamily(
- Arrays.asList(fooFont, barFont), "sans-serif", null,
+ Arrays.asList(fooFont, barFont), null,
FontConfig.FontFamily.VARIANT_DEFAULT);
- return new FontConfig(Collections.singletonList(family),
- Collections.emptyList(), 0, 1);
+ return new FontConfig(Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.singletonList(new FontConfig.NamedFamilyList(
+ Collections.singletonList(family), "sans-serif")), 0, 1);
};
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
@@ -411,7 +414,8 @@
assertThat(dir.getPostScriptMap()).containsKey("foo");
assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(dir.getFontFamilyMap()).containsKey("foobar");
- FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar");
+ assertThat(dir.getFontFamilyMap().get("foobar").getFamilies().size()).isEqualTo(1);
+ FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar").getFamilies().get(0);
assertThat(foobar.getFontList()).hasSize(1);
assertThat(foobar.getFontList().get(0).getFile())
.isEqualTo(dir.getPostScriptMap().get("foo"));
@@ -485,10 +489,12 @@
file, null, "bar", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT),
0, null, null);
FontConfig.FontFamily family = new FontConfig.FontFamily(
- Collections.singletonList(font), "sans-serif", null,
- FontConfig.FontFamily.VARIANT_DEFAULT);
- return new FontConfig(Collections.singletonList(family),
- Collections.emptyList(), 0, 1);
+ Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT);
+ return new FontConfig(
+ Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.singletonList(new FontConfig.NamedFamilyList(
+ Collections.singletonList(family), "sans-serif")), 0, 1);
});
dir.loadFontFileMap();
@@ -636,9 +642,10 @@
file, null, "test", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null,
null);
FontConfig.FontFamily family = new FontConfig.FontFamily(
- Collections.singletonList(font), "sans-serif", null,
- FontConfig.FontFamily.VARIANT_DEFAULT);
- return new FontConfig(Collections.singletonList(family), Collections.emptyList(), 0, 1);
+ Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT);
+ return new FontConfig(Collections.emptyList(), Collections.emptyList(),
+ Collections.singletonList(new FontConfig.NamedFamilyList(
+ Collections.singletonList(family), "sans-serif")), 0, 1);
});
dir.loadFontFileMap();
@@ -873,13 +880,14 @@
+ "</family>")));
assertThat(dir.getPostScriptMap()).containsKey("test");
assertThat(dir.getFontFamilyMap()).containsKey("test");
- FontConfig.FontFamily test = dir.getFontFamilyMap().get("test");
+ assertThat(dir.getFontFamilyMap().get("test").getFamilies().size()).isEqualTo(1);
+ FontConfig.FontFamily test = dir.getFontFamilyMap().get("test").getFamilies().get(0);
assertThat(test.getFontList()).hasSize(1);
assertThat(test.getFontList().get(0).getFile())
.isEqualTo(dir.getPostScriptMap().get("test"));
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void addFontFamily_noName() throws Exception {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
@@ -891,12 +899,7 @@
newAddFontFamilyRequest("<family lang='en'>"
+ " <font>test.ttf</font>"
+ "</family>"));
- try {
- dir.update(requests);
- fail("Expect NullPointerException");
- } catch (NullPointerException e) {
- // Expect
- }
+ dir.update(requests);
}
@Test
@@ -955,17 +958,16 @@
dir.loadFontFileMap();
assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty();
FontConfig.FontFamily firstFontFamily = dir.getSystemFontConfig().getFontFamilies().get(0);
- assertThat(firstFontFamily.getName()).isNotEmpty();
dir.update(Arrays.asList(
newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE),
- newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>"
+ newAddFontFamilyRequest("<family name='sans-serif'>"
+ " <font>test.ttf</font>"
+ "</family>")));
FontConfig fontConfig = dir.getSystemFontConfig();
assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty();
assertThat(fontConfig.getFontFamilies().get(0)).isEqualTo(firstFontFamily);
- FontConfig.FontFamily updated = getLastFamily(fontConfig, firstFontFamily.getName());
+ FontConfig.FontFamily updated = getLastFamily(fontConfig, "sans-serif");
assertThat(updated.getFontList()).hasSize(1);
assertThat(updated.getFontList().get(0).getFile())
.isEqualTo(dir.getPostScriptMap().get("test"));
@@ -1005,7 +1007,9 @@
mParser.setInput(is, "UTF-8");
mParser.nextTag();
- FontConfig.FontFamily fontFamily = FontListParser.readFamily(mParser, "", null, true);
+ FontConfig.NamedFamilyList namedFamilyList = FontListParser.readNamedFamily(
+ mParser, "", null, true);
+ FontConfig.FontFamily fontFamily = namedFamilyList.getFamilies().get(0);
List<FontUpdateRequest.Font> fonts = new ArrayList<>();
for (FontConfig.Font font : fontFamily.getFontList()) {
String name = font.getFile().getName();
@@ -1014,7 +1018,8 @@
psName, font.getStyle(), font.getTtcIndex(), font.getFontVariationSettings());
fonts.add(updateFont);
}
- FontUpdateRequest.Family family = new FontUpdateRequest.Family(fontFamily.getName(), fonts);
+ FontUpdateRequest.Family family = new FontUpdateRequest.Family(
+ namedFamilyList.getName(), fonts);
return new FontUpdateRequest(family);
}
@@ -1035,18 +1040,18 @@
// Returns the last family with the given name, which will be used for creating Typeface.
private static FontConfig.FontFamily getLastFamily(FontConfig fontConfig, String familyName) {
- List<FontConfig.FontFamily> fontFamilies = fontConfig.getFontFamilies();
- for (int i = fontFamilies.size() - 1; i >= 0; i--) {
- if (familyName.equals(fontFamilies.get(i).getName())) {
- return fontFamilies.get(i);
+ List<FontConfig.NamedFamilyList> namedFamilyLists = fontConfig.getNamedFamilyLists();
+ for (int i = namedFamilyLists.size() - 1; i >= 0; i--) {
+ if (familyName.equals(namedFamilyLists.get(i).getName())) {
+ return namedFamilyLists.get(i).getFamilies().get(0);
}
}
return null;
}
private static void assertNamedFamilyExists(FontConfig fontConfig, String familyName) {
- assertThat(fontConfig.getFontFamilies().stream()
- .map(FontConfig.FontFamily::getName)
+ assertThat(fontConfig.getNamedFamilyLists().stream()
+ .map(FontConfig.NamedFamilyList::getName)
.collect(Collectors.toSet())).contains(familyName);
}
}
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 650686f..fa5b7c1 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -69,6 +69,7 @@
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
/**
* Tests if fonts can be updated by {@link FontManager} API.
@@ -246,7 +247,10 @@
@Test
public void updateFontFamily() throws Exception {
assertThat(updateNotoSerifAs("serif")).isEqualTo(FontManager.RESULT_SUCCESS);
- FontConfig.FontFamily family = findFontFamilyOrThrow("serif");
+ final FontConfig.NamedFamilyList namedFamilyList = findFontFamilyOrThrow("serif");
+ assertThat(namedFamilyList.getFamilies().size()).isEqualTo(1);
+ final FontConfig.FontFamily family = namedFamilyList.getFamilies().get(0);
+
assertThat(family.getFontList()).hasSize(2);
assertThat(family.getFontList().get(0).getPostScriptName())
.isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
@@ -265,7 +269,10 @@
public void updateFontFamily_asNewFont() throws Exception {
assertThat(updateNotoSerifAs("UpdatableSystemFontTest-serif"))
.isEqualTo(FontManager.RESULT_SUCCESS);
- FontConfig.FontFamily family = findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ final FontConfig.NamedFamilyList namedFamilyList =
+ findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ assertThat(namedFamilyList.getFamilies().size()).isEqualTo(1);
+ final FontConfig.FontFamily family = namedFamilyList.getFamilies().get(0);
assertThat(family.getFontList()).hasSize(2);
assertThat(family.getFontList().get(0).getPostScriptName())
.isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
@@ -434,9 +441,15 @@
private String getFontPath(String psName) {
FontConfig fontConfig =
SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
- return fontConfig.getFontFamilies().stream()
+ final List<FontConfig.FontFamily> namedFamilies = fontConfig.getNamedFamilyLists().stream()
+ .flatMap(namedFamily -> namedFamily.getFamilies().stream()).toList();
+
+ return Stream.concat(fontConfig.getFontFamilies().stream(), namedFamilies.stream())
.flatMap(family -> family.getFontList().stream())
- .filter(font -> psName.equals(font.getPostScriptName()))
+ .filter(font -> {
+ Log.e("Debug", "PsName = " + font.getPostScriptName());
+ return psName.equals(font.getPostScriptName());
+ })
// Return the last match, because the latter family takes precedence if two families
// have the same name.
.reduce((first, second) -> second)
@@ -445,10 +458,10 @@
.getAbsolutePath();
}
- private FontConfig.FontFamily findFontFamilyOrThrow(String familyName) {
+ private FontConfig.NamedFamilyList findFontFamilyOrThrow(String familyName) {
FontConfig fontConfig =
SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
- return fontConfig.getFontFamilies().stream()
+ return fontConfig.getNamedFamilyLists().stream()
.filter(family -> familyName.equals(family.getName()))
// Return the last match, because the latter family takes precedence if two families
// have the same name.