1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics; 18 19 import android.text.FontConfig; 20 import android.graphics.fonts.FontVariationAxis; 21 import android.util.Xml; 22 23 import org.xmlpull.v1.XmlPullParser; 24 import org.xmlpull.v1.XmlPullParserException; 25 26 import android.annotation.Nullable; 27 import com.android.internal.annotations.VisibleForTesting; 28 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.regex.Pattern; 34 35 /** 36 * Parser for font config files. 37 * 38 * @hide 39 */ 40 public class FontListParser { 41 42 /* Parse fallback list (no names) */ parse(InputStream in)43 public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { 44 try { 45 XmlPullParser parser = Xml.newPullParser(); 46 parser.setInput(in, null); 47 parser.nextTag(); 48 return readFamilies(parser); 49 } finally { 50 in.close(); 51 } 52 } 53 readFamilies(XmlPullParser parser)54 private static FontConfig readFamilies(XmlPullParser parser) 55 throws XmlPullParserException, IOException { 56 List<FontConfig.Family> families = new ArrayList<>(); 57 List<FontConfig.Alias> aliases = new ArrayList<>(); 58 59 parser.require(XmlPullParser.START_TAG, null, "familyset"); 60 while (parser.next() != XmlPullParser.END_TAG) { 61 if (parser.getEventType() != XmlPullParser.START_TAG) continue; 62 String tag = parser.getName(); 63 if (tag.equals("family")) { 64 families.add(readFamily(parser)); 65 } else if (tag.equals("alias")) { 66 aliases.add(readAlias(parser)); 67 } else { 68 skip(parser); 69 } 70 } 71 return new FontConfig(families.toArray(new FontConfig.Family[families.size()]), 72 aliases.toArray(new FontConfig.Alias[aliases.size()])); 73 } 74 readFamily(XmlPullParser parser)75 private static FontConfig.Family readFamily(XmlPullParser parser) 76 throws XmlPullParserException, IOException { 77 String name = parser.getAttributeValue(null, "name"); 78 String lang = parser.getAttributeValue(null, "lang"); 79 String variant = parser.getAttributeValue(null, "variant"); 80 List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>(); 81 while (parser.next() != XmlPullParser.END_TAG) { 82 if (parser.getEventType() != XmlPullParser.START_TAG) continue; 83 String tag = parser.getName(); 84 if (tag.equals("font")) { 85 fonts.add(readFont(parser)); 86 } else { 87 skip(parser); 88 } 89 } 90 int intVariant = FontConfig.Family.VARIANT_DEFAULT; 91 if (variant != null) { 92 if (variant.equals("compact")) { 93 intVariant = FontConfig.Family.VARIANT_COMPACT; 94 } else if (variant.equals("elegant")) { 95 intVariant = FontConfig.Family.VARIANT_ELEGANT; 96 } 97 } 98 return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang, 99 intVariant); 100 } 101 102 /** Matches leading and trailing XML whitespace. */ 103 private static final Pattern FILENAME_WHITESPACE_PATTERN = 104 Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); 105 readFont(XmlPullParser parser)106 private static FontConfig.Font readFont(XmlPullParser parser) 107 throws XmlPullParserException, IOException { 108 String indexStr = parser.getAttributeValue(null, "index"); 109 int index = indexStr == null ? 0 : Integer.parseInt(indexStr); 110 List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); 111 String weightStr = parser.getAttributeValue(null, "weight"); 112 int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); 113 boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); 114 StringBuilder filename = new StringBuilder(); 115 while (parser.next() != XmlPullParser.END_TAG) { 116 if (parser.getEventType() == XmlPullParser.TEXT) { 117 filename.append(parser.getText()); 118 } 119 if (parser.getEventType() != XmlPullParser.START_TAG) continue; 120 String tag = parser.getName(); 121 if (tag.equals("axis")) { 122 axes.add(readAxis(parser)); 123 } else { 124 skip(parser); 125 } 126 } 127 String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); 128 return new FontConfig.Font(sanitizedName, index, 129 axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic); 130 } 131 readAxis(XmlPullParser parser)132 private static FontVariationAxis readAxis(XmlPullParser parser) 133 throws XmlPullParserException, IOException { 134 String tagStr = parser.getAttributeValue(null, "tag"); 135 String styleValueStr = parser.getAttributeValue(null, "stylevalue"); 136 skip(parser); // axis tag is empty, ignore any contents and consume end tag 137 return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr)); 138 } 139 readAlias(XmlPullParser parser)140 private static FontConfig.Alias readAlias(XmlPullParser parser) 141 throws XmlPullParserException, IOException { 142 String name = parser.getAttributeValue(null, "name"); 143 String toName = parser.getAttributeValue(null, "to"); 144 String weightStr = parser.getAttributeValue(null, "weight"); 145 int weight; 146 if (weightStr == null) { 147 weight = 400; 148 } else { 149 weight = Integer.parseInt(weightStr); 150 } 151 skip(parser); // alias tag is empty, ignore any contents and consume end tag 152 return new FontConfig.Alias(name, toName, weight); 153 } 154 skip(XmlPullParser parser)155 private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { 156 int depth = 1; 157 while (depth > 0) { 158 switch (parser.next()) { 159 case XmlPullParser.START_TAG: 160 depth++; 161 break; 162 case XmlPullParser.END_TAG: 163 depth--; 164 break; 165 } 166 } 167 } 168 } 169