Merge "Support multiple languages per font in fonts.xml"

This commit is contained in:
Roozbeh Pournader
2017-08-11 23:27:50 +00:00
committed by Android (Google) Code Review
7 changed files with 87 additions and 38 deletions

View File

@@ -175,7 +175,7 @@ public final class FontConfig {
public static final class Family {
private final @NonNull String mName;
private final @NonNull Font[] mFonts;
private final @NonNull String mLanguage;
private final @NonNull String[] mLanguages;
/** @hide */
@Retention(SOURCE)
@@ -209,11 +209,11 @@ public final class FontConfig {
// See frameworks/minikin/include/minikin/FontFamily.h
private final @Variant int mVariant;
public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String language,
public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String[] languages,
@Variant int variant) {
mName = name;
mFonts = fonts;
mLanguage = language;
mLanguages = languages;
mVariant = variant;
}
@@ -232,10 +232,10 @@ public final class FontConfig {
}
/**
* Returns the language for this family. May be null.
* Returns the languages for this family. May be null.
*/
public @Nullable String getLanguage() {
return mLanguage;
public @Nullable String[] getLanguages() {
return mLanguages;
}
/**

View File

@@ -51,10 +51,10 @@ struct NativeFamilyBuilder {
std::vector<minikin::FontVariation> axes;
};
static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, jint variant) {
NativeFamilyBuilder* builder;
if (lang != nullptr) {
ScopedUtfChars str(env, lang);
if (langs != nullptr) {
ScopedUtfChars str(env, langs);
builder = new NativeFamilyBuilder(
minikin::FontStyle::registerLanguageList(str.c_str()), variant);
} else {

View File

@@ -40,6 +40,7 @@ import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -128,7 +129,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_NonExistentFontShouldBeIgnored() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " <font weight='400' style='normal'>NoSuchFont.ttf</font>"
@@ -154,7 +155,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_NamedFamily() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
@@ -200,7 +201,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_defaultFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
@@ -239,7 +240,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_namedFallbackFamily() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
@@ -291,7 +292,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_namedFallbackFamily2() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
@@ -341,7 +342,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_ImplicitSansSerifFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
@@ -380,7 +381,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_ElegantFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
@@ -418,7 +419,7 @@ public class TypefaceSystemFallbackTest {
@Test
public void testBuildSystemFallback_ElegantFallback_customFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
@@ -466,4 +467,42 @@ public class TypefaceSystemFallbackTest {
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
}
@Test
public void testBuildSystemFallback_multiLingualFamilies() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family lang='de'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ " <family lang='it fr'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
paint.setTypeface(fontMap.get("sans-serif"));
paint.setTextLocale(Locale.GERMANY);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setTextLocale(Locale.ITALY);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setTextLocale(Locale.FRANCE);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
}

View File

@@ -19,7 +19,7 @@
effectively add 300 to the weight, this ensures that 900 is the bold
paired with the 500 weight, ensuring adequate contrast.
-->
<familyset version="22">
<familyset version="23">
<!-- first font is default -->
<family name="sans-serif">
<font weight="100" style="normal">Roboto-Thin.ttf</font>
@@ -128,8 +128,7 @@
<font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
</family>
<!-- TODO: add Geok -->
<family lang="und-Geor">
<family lang="und-Geor und-Geok">
<font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
</family>
@@ -474,8 +473,7 @@
<family lang="zh-Hans">
<font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
</family>
<!-- TODO: Add Bopo -->
<family lang="zh-Hant">
<family lang="zh-Hant zh-Bopo">
<font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
</family>
<family lang="ja">

View File

@@ -16,9 +16,11 @@
package android.graphics;
import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.graphics.fonts.FontVariationAxis;
import android.text.FontConfig;
import android.text.TextUtils;
import android.util.Log;
import dalvik.annotation.optimization.CriticalNative;
@@ -48,8 +50,16 @@ public class FontFamily {
mBuilderPtr = nInitBuilder(null, 0);
}
public FontFamily(String lang, int variant) {
mBuilderPtr = nInitBuilder(lang, variant);
public FontFamily(@Nullable String[] langs, int variant) {
final String langsString;
if (langs == null || langs.length == 0) {
langsString = null;
} else if (langs.length == 1) {
langsString = langs[0];
} else {
langsString = TextUtils.join(",", langs);
}
mBuilderPtr = nInitBuilder(langsString, variant);
}
/**
@@ -174,7 +184,7 @@ public class FontFamily {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
}
private static native long nInitBuilder(String lang, int variant);
private static native long nInitBuilder(String langs, int variant);
@CriticalNative
private static native long nCreateFamily(long mBuilderPtr);

View File

@@ -74,13 +74,14 @@ public class FontListParser {
private static FontConfig.Family readFamily(XmlPullParser parser)
throws XmlPullParserException, IOException {
String name = parser.getAttributeValue(null, "name");
String lang = parser.getAttributeValue(null, "lang");
String variant = parser.getAttributeValue(null, "variant");
List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
final String name = parser.getAttributeValue(null, "name");
final String lang = parser.getAttributeValue(null, "lang");
final String[] langs = lang == null ? null : lang.split("\\s+");
final String variant = parser.getAttributeValue(null, "variant");
final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
final String tag = parser.getName();
if (tag.equals("font")) {
fonts.add(readFont(parser));
} else {
@@ -95,7 +96,7 @@ public class FontListParser {
intVariant = FontConfig.Family.VARIANT_ELEGANT;
}
}
return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang,
return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), langs,
intVariant);
}

View File

@@ -929,9 +929,9 @@ public class Typeface {
}
private static @Nullable FontFamily createFontFamily(
String familyName, List<FontConfig.Font> fonts, String languageTag, int variant,
String familyName, List<FontConfig.Font> fonts, String[] languageTags, int variant,
Map<String, ByteBuffer> cache, String fontDir) {
final FontFamily family = new FontFamily(languageTag, variant);
final FontFamily family = new FontFamily(languageTags, variant);
for (int i = 0; i < fonts.size(); i++) {
final FontConfig.Font font = fonts.get(i);
final String fullPath = fontDir + font.getFontName();
@@ -952,7 +952,8 @@ public class Typeface {
}
}
if (!family.freeze()) {
Log.e(TAG, "Unable to load Family: " + familyName + " : " + languageTag);
Log.e(TAG, "Unable to load Family: " + familyName + " : "
+ Arrays.toString(languageTags));
return null;
}
return family;
@@ -963,7 +964,7 @@ public class Typeface {
Map<String, ByteBuffer> cache,
String fontDir) {
final String languageTag = xmlFamily.getLanguage();
final String[] languageTags = xmlFamily.getLanguages();
final int variant = xmlFamily.getVariant();
final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>();
@@ -985,7 +986,7 @@ public class Typeface {
}
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
xmlFamily.getName(), defaultFonts, languageTag, variant, cache, fontDir);
xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir);
// Insert family into fallback map.
for (int i = 0; i < fallbackMap.size(); i++) {
@@ -997,7 +998,7 @@ public class Typeface {
}
} else {
final FontFamily family = createFontFamily(
xmlFamily.getName(), fallback, languageTag, variant, cache, fontDir);
xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir);
if (family != null) {
fallbackMap.valueAt(i).add(family);
}
@@ -1034,7 +1035,7 @@ public class Typeface {
}
final FontFamily family = createFontFamily(
xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
xmlFamily.getLanguage(), xmlFamily.getVariant(), bufferCache, fontDir);
xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir);
if (family == null) {
continue;
}