summaryrefslogtreecommitdiff
path: root/graphics/java/android
diff options
context:
space:
mode:
author Seigo Nonaka <nona@google.com> 2023-10-10 00:14:07 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-10-10 00:14:07 +0000
commit1e08e96e7c324e87cf87282d3e8f058e1e177eb5 (patch)
tree98c1be902fc7555f7366139e3ac230612f1e9064 /graphics/java/android
parenteb7e841ed363818f44a354f41a751bd43a468a55 (diff)
parente7455a812fab18e6c3d0e8ea3fe701c1c6e4216d (diff)
Merge "Support Locale Fallback Family Customization" into main
Diffstat (limited to 'graphics/java/android')
-rw-r--r--graphics/java/android/graphics/FontListParser.java4
-rw-r--r--graphics/java/android/graphics/fonts/FontCustomizationParser.java46
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java97
3 files changed, 138 insertions, 9 deletions
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 735bc180c015..52b0b95d3e76 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -236,7 +236,9 @@ public class FontListParser {
}
}
- return new FontConfig(families, filtered, resultNamedFamilies, lastModifiedDate,
+ return new FontConfig(families, filtered, resultNamedFamilies,
+ customization.getLocaleFamilyCustomizations(),
+ lastModifiedDate,
configVersion);
}
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index b458dd9021d0..6e04a2f5e405 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -22,6 +22,7 @@ import static android.text.FontConfig.NamedFamilyList;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
+import android.text.FontConfig;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -34,6 +35,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
/**
@@ -52,14 +54,19 @@ public class FontCustomizationParser {
private final List<Alias> mAdditionalAliases;
+ private final List<FontConfig.Customization.LocaleFallback> mLocaleFamilyCustomizations;
+
public Result() {
mAdditionalNamedFamilies = Collections.emptyMap();
+ mLocaleFamilyCustomizations = Collections.emptyList();
mAdditionalAliases = Collections.emptyList();
}
public Result(Map<String, NamedFamilyList> additionalNamedFamilies,
+ List<FontConfig.Customization.LocaleFallback> localeFamilyCustomizations,
List<Alias> additionalAliases) {
mAdditionalNamedFamilies = additionalNamedFamilies;
+ mLocaleFamilyCustomizations = localeFamilyCustomizations;
mAdditionalAliases = additionalAliases;
}
@@ -70,6 +77,10 @@ public class FontCustomizationParser {
public List<Alias> getAdditionalAliases() {
return mAdditionalAliases;
}
+
+ public List<FontConfig.Customization.LocaleFallback> getLocaleFamilyCustomizations() {
+ return mLocaleFamilyCustomizations;
+ }
}
/**
@@ -89,7 +100,9 @@ public class FontCustomizationParser {
}
private static Result validateAndTransformToResult(
- List<NamedFamilyList> families, List<Alias> aliases) {
+ List<NamedFamilyList> families,
+ List<FontConfig.Customization.LocaleFallback> outLocaleFamilies,
+ List<Alias> aliases) {
HashMap<String, NamedFamilyList> namedFamily = new HashMap<>();
for (int i = 0; i < families.size(); ++i) {
final NamedFamilyList family = families.get(i);
@@ -105,7 +118,7 @@ public class FontCustomizationParser {
+ "requires fallackTarget attribute");
}
}
- return new Result(namedFamily, aliases);
+ return new Result(namedFamily, outLocaleFamilies, aliases);
}
private static Result readFamilies(
@@ -115,12 +128,13 @@ public class FontCustomizationParser {
) throws XmlPullParserException, IOException {
List<NamedFamilyList> families = new ArrayList<>();
List<Alias> aliases = new ArrayList<>();
+ List<FontConfig.Customization.LocaleFallback> outLocaleFamilies = new ArrayList<>();
parser.require(XmlPullParser.START_TAG, null, "fonts-modification");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("family")) {
- readFamily(parser, fontDir, families, updatableFontMap);
+ readFamily(parser, fontDir, families, outLocaleFamilies, updatableFontMap);
} else if (tag.equals("family-list")) {
readFamilyList(parser, fontDir, families, updatableFontMap);
} else if (tag.equals("alias")) {
@@ -129,13 +143,14 @@ public class FontCustomizationParser {
FontListParser.skip(parser);
}
}
- return validateAndTransformToResult(families, aliases);
+ return validateAndTransformToResult(families, outLocaleFamilies, aliases);
}
private static void readFamily(
@NonNull XmlPullParser parser,
@NonNull String fontDir,
@NonNull List<NamedFamilyList> out,
+ @NonNull List<FontConfig.Customization.LocaleFallback> outCustomization,
@Nullable Map<String, File> updatableFontMap)
throws XmlPullParserException, IOException {
final String customizationType = parser.getAttributeValue(null, "customizationType");
@@ -148,6 +163,29 @@ public class FontCustomizationParser {
if (fontFamily != null) {
out.add(fontFamily);
}
+ } else if (customizationType.equals("new-locale-family")) {
+ final String lang = parser.getAttributeValue(null, "lang");
+ final String op = parser.getAttributeValue(null, "operation");
+ final int intOp;
+ if (op.equals("append")) {
+ intOp = FontConfig.Customization.LocaleFallback.OPERATION_APPEND;
+ } else if (op.equals("prepend")) {
+ intOp = FontConfig.Customization.LocaleFallback.OPERATION_PREPEND;
+ } else if (op.equals("replace")) {
+ intOp = FontConfig.Customization.LocaleFallback.OPERATION_REPLACE;
+ } else {
+ throw new IllegalArgumentException("Unknown operation=" + op);
+ }
+
+ final FontConfig.FontFamily family = FontListParser.readFamily(
+ parser, fontDir, updatableFontMap, false);
+
+ // For ignoring the customization, consume the new-locale-family element but don't
+ // register any customizations.
+ if (com.android.text.flags.Flags.customLocaleFallback()) {
+ outCustomization.add(new FontConfig.Customization.LocaleFallback(
+ Locale.forLanguageTag(lang), intOp, family));
+ }
} else {
throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index d4e35b30c8d0..618aa5b5019c 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -16,10 +16,15 @@
package android.graphics.fonts;
+import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_APPEND;
+import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_PREPEND;
+import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_REPLACE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
import android.graphics.Typeface;
+import android.os.LocaleList;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -38,6 +43,7 @@ import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -119,7 +125,6 @@ public final class SystemFonts {
}
}
-
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
defaultFonts, languageTags, variant, xmlFamily.getVariableFontFamilyType(), false,
cache);
@@ -300,11 +305,11 @@ public final class SystemFonts {
} catch (IOException e) {
Log.e(TAG, "Failed to open/read system font configurations.", e);
return new FontConfig(Collections.emptyList(), Collections.emptyList(),
- Collections.emptyList(), 0, 0);
+ 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(),
- Collections.emptyList(), 0, 0);
+ Collections.emptyList(), Collections.emptyList(), 0, 0);
}
}
@@ -328,6 +333,8 @@ public final class SystemFonts {
ArrayMap<String, ByteBuffer> outBufferCache) {
final ArrayMap<String, NativeFamilyListSet> fallbackListMap = new ArrayMap<>();
+ final List<FontConfig.Customization.LocaleFallback> localeFallbacks =
+ fontConfig.getLocaleFallbackCustomizations();
final List<FontConfig.NamedFamilyList> namedFamilies = fontConfig.getNamedFamilyLists();
for (int i = 0; i < namedFamilies.size(); ++i) {
@@ -336,10 +343,54 @@ public final class SystemFonts {
}
// Then, add fallback fonts to the fallback map.
+ final List<FontConfig.Customization.LocaleFallback> customizations = new ArrayList<>();
final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
+ final SparseIntArray seenCustomization = new SparseIntArray();
for (int i = 0; i < xmlFamilies.size(); i++) {
final FontConfig.FontFamily xmlFamily = xmlFamilies.get(i);
- pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
+
+ customizations.clear();
+ for (int j = 0; j < localeFallbacks.size(); ++j) {
+ if (seenCustomization.get(j, -1) != -1) {
+ continue; // The customization is already applied.
+ }
+ FontConfig.Customization.LocaleFallback localeFallback = localeFallbacks.get(j);
+ if (scriptMatch(xmlFamily.getLocaleList(), localeFallback.getScript())) {
+ customizations.add(localeFallback);
+ seenCustomization.put(j, 1);
+ }
+ }
+
+ if (customizations.isEmpty()) {
+ pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
+ } else {
+ for (int j = 0; j < customizations.size(); ++j) {
+ FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j);
+ if (localeFallback.getOperation() == OPERATION_PREPEND) {
+ pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap,
+ outBufferCache);
+ }
+ }
+ boolean isReplaced = false;
+ for (int j = 0; j < customizations.size(); ++j) {
+ FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j);
+ if (localeFallback.getOperation() == OPERATION_REPLACE) {
+ pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap,
+ outBufferCache);
+ isReplaced = true;
+ }
+ }
+ if (!isReplaced) { // If nothing is replaced, push the original one.
+ pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
+ }
+ for (int j = 0; j < customizations.size(); ++j) {
+ FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j);
+ if (localeFallback.getOperation() == OPERATION_APPEND) {
+ pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap,
+ outBufferCache);
+ }
+ }
+ }
}
// Build the font map and fallback map.
@@ -365,4 +416,42 @@ public final class SystemFonts {
Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
return result;
}
+
+ private static boolean scriptMatch(LocaleList localeList, String targetScript) {
+ if (localeList == null || localeList.isEmpty()) {
+ return false;
+ }
+ for (int i = 0; i < localeList.size(); ++i) {
+ Locale locale = localeList.get(i);
+ if (locale == null) {
+ continue;
+ }
+ String baseScript = FontConfig.resolveScript(locale);
+ if (baseScript.equals(targetScript)) {
+ return true;
+ }
+
+ // Subtag match
+ if (targetScript.equals("Bopo") && baseScript.equals("Hanb")) {
+ // Hanb is Han with Bopomofo.
+ return true;
+ } else if (targetScript.equals("Hani")) {
+ if (baseScript.equals("Hanb") || baseScript.equals("Hans")
+ || baseScript.equals("Hant") || baseScript.equals("Kore")
+ || baseScript.equals("Jpan")) {
+ // Han id suppoted by Taiwanese, Traditional Chinese, Simplified Chinese, Korean
+ // and Japanese.
+ return true;
+ }
+ } else if (targetScript.equals("Hira") || targetScript.equals("Hrkt")
+ || targetScript.equals("Kana")) {
+ if (baseScript.equals("Jpan") || baseScript.equals("Hrkt")) {
+ // Hiragana, Hiragana-Katakana, Katakana is supported by Japanese and
+ // Hiragana-Katakana script.
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}