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
This commit is contained in:
@@ -16178,6 +16178,7 @@ package android.text {
|
||||
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 @@ package android.text {
|
||||
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 @@ package android.text {
|
||||
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 {
|
||||
|
||||
@@ -2706,6 +2706,7 @@ package android.text {
|
||||
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 @@ package android.text {
|
||||
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 @@ package android.text {
|
||||
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);
|
||||
|
||||
@@ -36,6 +36,7 @@ import android.os.Parcelable;
|
||||
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 @@ import java.util.Objects;
|
||||
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,13 +69,24 @@ public final class FontConfig implements Parcelable {
|
||||
* @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.
|
||||
*
|
||||
@@ -94,6 +107,10 @@ public final class FontConfig implements Parcelable {
|
||||
return mAliases;
|
||||
}
|
||||
|
||||
public @NonNull List<NamedFamilyList> getNamedFamilyLists() {
|
||||
return mNamedFamilyLists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modified time in milliseconds.
|
||||
*
|
||||
@@ -133,8 +150,9 @@ public final class FontConfig implements Parcelable {
|
||||
|
||||
@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 final class FontConfig implements Parcelable {
|
||||
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 final class FontConfig implements Parcelable {
|
||||
*/
|
||||
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 @@ public final class FontConfig implements Parcelable {
|
||||
*
|
||||
* @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 @@ public final class FontConfig implements Parcelable {
|
||||
*
|
||||
* 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 @@ public final class FontConfig implements Parcelable {
|
||||
|
||||
@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 @@ public final class FontConfig implements Parcelable {
|
||||
|
||||
@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 @@ public final class FontConfig implements Parcelable {
|
||||
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 + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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");
|
||||
|
||||
Binary file not shown.
@@ -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");
|
||||
|
||||
Binary file not shown.
@@ -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");
|
||||
|
||||
BIN
core/tests/coretests/assets/fonts/fallback.ttf
Normal file
BIN
core/tests/coretests/assets/fonts/fallback.ttf
Normal file
Binary file not shown.
205
core/tests/coretests/assets/fonts/fallback.ttx
Normal file
205
core/tests/coretests/assets/fonts/fallback.ttx
Normal file
@@ -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>
|
||||
BIN
core/tests/coretests/assets/fonts/fallback_capital.ttf
Normal file
BIN
core/tests/coretests/assets/fonts/fallback_capital.ttf
Normal file
Binary file not shown.
205
core/tests/coretests/assets/fonts/fallback_capital.ttx
Normal file
205
core/tests/coretests/assets/fonts/fallback_capital.ttx
Normal file
@@ -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>
|
||||
@@ -47,6 +47,7 @@ import java.io.IOException;
|
||||
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 @@ public final class FontListParserTest {
|
||||
+ "<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 @@ public final class FontListParserTest {
|
||||
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 @@ public final class FontListParserTest {
|
||||
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 @@ public final class FontListParserTest {
|
||||
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 @@ public final class FontListParserTest {
|
||||
+ " <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 @@ public final class FontListParserTest {
|
||||
+ " <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 @@ public final class FontListParserTest {
|
||||
+ " <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 @@ public final class FontListParserTest {
|
||||
+ " <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 @@ public final class FontListParserTest {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import android.content.res.AssetManager;
|
||||
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 @@ public class TypefaceSystemFallbackTest {
|
||||
"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 @@ public class TypefaceSystemFallbackTest {
|
||||
|
||||
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 @@ public class TypefaceSystemFallbackTest {
|
||||
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'?>"
|
||||
|
||||
@@ -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.FileInputStream;
|
||||
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 @@ import java.util.regex.Pattern;
|
||||
* @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 @@ public class FontListParser {
|
||||
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 @@ public class FontListParser {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class FontListParser {
|
||||
}
|
||||
}
|
||||
|
||||
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 class FontListParser {
|
||||
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 @@ public class FontListParser {
|
||||
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. */
|
||||
|
||||
@@ -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 @@ import java.util.Map;
|
||||
* @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 @@ public class FontCustomizationParser {
|
||||
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 @@ public class FontCustomizationParser {
|
||||
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 @@ public class FontCustomizationParser {
|
||||
@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 @@ public class FontCustomizationParser {
|
||||
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 @@ public class FontCustomizationParser {
|
||||
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);
|
||||
|
||||
@@ -114,17 +114,19 @@ public final class FontFamily {
|
||||
* @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 @@ public final class FontFamily {
|
||||
@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();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.graphics.FontListParser;
|
||||
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 @@ public final class SystemFonts {
|
||||
}
|
||||
|
||||
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 @@ public final class SystemFonts {
|
||||
}
|
||||
|
||||
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 @@ public final class SystemFonts {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public final class SystemFonts {
|
||||
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 @@ public final class SystemFonts {
|
||||
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 @@ public final class SystemFonts {
|
||||
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]));
|
||||
}
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ static void FontFamily_Builder_addFont(CRITICAL_JNI_PARAMS_COMMA jlong builderPt
|
||||
|
||||
// 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 @@ static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderP
|
||||
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 jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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[] = {
|
||||
|
||||
@@ -163,19 +163,25 @@ public class FontManagerShellCommand extends ShellCommand {
|
||||
// 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();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.android.server.graphics.fonts;
|
||||
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.Files;
|
||||
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 @@ final class UpdatableFontDir {
|
||||
|
||||
// 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 @@ final class UpdatableFontDir {
|
||||
}
|
||||
}
|
||||
}
|
||||
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 @@ final class UpdatableFontDir {
|
||||
}
|
||||
}
|
||||
|
||||
@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 @@ final class UpdatableFontDir {
|
||||
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 @@ final class UpdatableFontDir {
|
||||
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 @@ final class UpdatableFontDir {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -210,7 +210,8 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
+ "</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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
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 @@ public final class UpdatableFontDirTest {
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ import java.util.Arrays;
|
||||
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 @@ public class UpdatableSystemFontTest {
|
||||
@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 class UpdatableSystemFontTest {
|
||||
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 @@ public class UpdatableSystemFontTest {
|
||||
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 @@ public class UpdatableSystemFontTest {
|
||||
.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.
|
||||
|
||||
Reference in New Issue
Block a user