Files
frameworks_base/graphics/java/android/graphics/FontListParser.java
Kohsuke Yatoh 87036d61ec Check updatable font dir in FontListParser.
If updatable font dir is provided by caller and fonts exist in the dir,
FontListParser will use the updated fonts.
Currently, updatable font dir is used only in test.

Bug: 173619554
Test: atest TypefaceSystemFallbackTest
Change-Id: I631764fe235d258b6b5cb6d6906c17d48c7e4608
2021-01-11 16:11:53 +00:00

201 lines
7.8 KiB
Java

/*
* Copyright (C) 2014 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.
*/
package android.graphics;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.text.FontConfig;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* Parser for font config files.
*
* @hide
*/
public class FontListParser {
/* Parse fallback list (no names) */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
return parse(in, "/system/fonts", null);
}
/**
* Parse the fonts.xml
*/
public static FontConfig parse(InputStream in, String fontDir,
@Nullable String updatableFontDir) throws XmlPullParserException, IOException {
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parser.nextTag();
return readFamilies(parser, fontDir, updatableFontDir);
} finally {
in.close();
}
}
private static FontConfig readFamilies(XmlPullParser parser, String fontDir,
@Nullable String updatableFontDir) throws XmlPullParserException, IOException {
List<FontConfig.Family> families = new ArrayList<>();
List<FontConfig.Alias> aliases = new ArrayList<>();
parser.require(XmlPullParser.START_TAG, null, "familyset");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("family")) {
families.add(readFamily(parser, fontDir, updatableFontDir));
} else if (tag.equals("alias")) {
aliases.add(readAlias(parser));
} else {
skip(parser);
}
}
return new FontConfig(families.toArray(new FontConfig.Family[families.size()]),
aliases.toArray(new FontConfig.Alias[aliases.size()]));
}
/**
* Reads a family element
*/
public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir,
@Nullable String updatableFontDir) throws XmlPullParserException, IOException {
final String name = parser.getAttributeValue(null, "name");
final String lang = parser.getAttributeValue("", "lang");
final String variant = parser.getAttributeValue(null, "variant");
final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
final String tag = parser.getName();
if (tag.equals("font")) {
fonts.add(readFont(parser, fontDir, updatableFontDir));
} else {
skip(parser);
}
}
int intVariant = FontConfig.Family.VARIANT_DEFAULT;
if (variant != null) {
if (variant.equals("compact")) {
intVariant = FontConfig.Family.VARIANT_COMPACT;
} else if (variant.equals("elegant")) {
intVariant = FontConfig.Family.VARIANT_ELEGANT;
}
}
return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang,
intVariant);
}
/** Matches leading and trailing XML whitespace. */
private static final Pattern FILENAME_WHITESPACE_PATTERN =
Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
private static FontConfig.Font readFont(XmlPullParser parser, String fontDir,
@Nullable String updatableFontDir) throws XmlPullParserException, IOException {
String indexStr = parser.getAttributeValue(null, "index");
int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>();
String weightStr = parser.getAttributeValue(null, "weight");
int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
StringBuilder filename = new StringBuilder();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
filename.append(parser.getText());
}
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("axis")) {
axes.add(readAxis(parser));
} else {
skip(parser);
}
}
String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
String fontName = findFontFile(sanitizedName, fontDir, updatableFontDir);
return new FontConfig.Font(fontName, index, axes.toArray(
new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
}
private static String findFontFile(String fileName, String fontDir,
@Nullable String updatableFontDir) {
if (updatableFontDir != null) {
String updatableFontName = updatableFontDir + fileName;
if (new File(updatableFontName).exists()) {
return updatableFontName;
}
}
return fontDir + fileName;
}
private static FontVariationAxis readAxis(XmlPullParser parser)
throws XmlPullParserException, IOException {
String tagStr = parser.getAttributeValue(null, "tag");
String styleValueStr = parser.getAttributeValue(null, "stylevalue");
skip(parser); // axis tag is empty, ignore any contents and consume end tag
return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
}
/**
* Reads alias elements
*/
public static FontConfig.Alias readAlias(XmlPullParser parser)
throws XmlPullParserException, IOException {
String name = parser.getAttributeValue(null, "name");
String toName = parser.getAttributeValue(null, "to");
String weightStr = parser.getAttributeValue(null, "weight");
int weight;
if (weightStr == null) {
weight = 400;
} else {
weight = Integer.parseInt(weightStr);
}
skip(parser); // alias tag is empty, ignore any contents and consume end tag
return new FontConfig.Alias(name, toName, weight);
}
/**
* Skip until next element
*/
public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
int depth = 1;
while (depth > 0) {
switch (parser.next()) {
case XmlPullParser.START_TAG:
depth++;
break;
case XmlPullParser.END_TAG:
depth--;
break;
}
}
}
}