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.graphics.fonts.FontVariationAxis; 20 import android.text.FontConfig; 21 import android.util.Xml; 22 23 import org.xmlpull.v1.XmlPullParser; 24 import org.xmlpull.v1.XmlPullParserException; 25 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.regex.Pattern; 31 32 /** 33 * Parser for font config files. 34 * 35 * @hide 36 */ 37 public class FontListParser { 38 39 /* Parse fallback list (no names) */ parse(InputStream in)40 public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { 41 try { 42 XmlPullParser parser = Xml.newPullParser(); 43 parser.setInput(in, null); 44 parser.nextTag(); 45 return readFamilies(parser); 46 } finally { 47 in.close(); 48 } 49 } 50 readFamilies(XmlPullParser parser)51 private static FontConfig readFamilies(XmlPullParser parser) 52 throws XmlPullParserException, IOException { 53 List<FontConfig.Family> families = new ArrayList<>(); 54 List<FontConfig.Alias> aliases = new ArrayList<>(); 55 56 parser.require(XmlPullParser.START_TAG, null, "familyset"); 57 while (parser.next() != XmlPullParser.END_TAG) { 58 if (parser.getEventType() != XmlPullParser.START_TAG) continue; 59 String tag = parser.getName(); 60 if (tag.equals("family")) { 61 families.add(readFamily(parser)); 62 } else if (tag.equals("alias")) { 63 aliases.add(readAlias(parser)); 64 } else { 65 skip(parser); 66 } 67 } 68 return new FontConfig(families.toArray(new FontConfig.Family[families.size()]), 69 aliases.toArray(new FontConfig.Alias[aliases.size()])); 70 } 71 readFamily(XmlPullParser parser)72 private static FontConfig.Family readFamily(XmlPullParser parser) 73 throws XmlPullParserException, IOException { 74 final String name = parser.getAttributeValue(null, "name"); 75 final String lang = parser.getAttributeValue(null, "lang"); 76 final String[] langs = lang == null ? null : lang.split("\\s+"); 77 final String variant = parser.getAttributeValue(null, "variant"); 78 final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>(); 79 while (parser.next() != XmlPullParser.END_TAG) { 80 if (parser.getEventType() != XmlPullParser.START_TAG) continue; 81 final String tag = parser.getName(); 82 if (tag.equals("font")) { 83 fonts.add(readFont(parser)); 84 } else { 85 skip(parser); 86 } 87 } 88 int intVariant = FontConfig.Family.VARIANT_DEFAULT; 89 if (variant != null) { 90 if (variant.equals("compact")) { 91 intVariant = FontConfig.Family.VARIANT_COMPACT; 92 } else if (variant.equals("elegant")) { 93 intVariant = FontConfig.Family.VARIANT_ELEGANT; 94 } 95 } 96 return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), langs, 97 intVariant); 98 } 99 100 /** Matches leading and trailing XML whitespace. */ 101 private static final Pattern FILENAME_WHITESPACE_PATTERN = 102 Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); 103 readFont(XmlPullParser parser)104 private static FontConfig.Font readFont(XmlPullParser parser) 105 throws XmlPullParserException, IOException { 106 String indexStr = parser.getAttributeValue(null, "index"); 107 int index = indexStr == null ? 0 : Integer.parseInt(indexStr); 108 List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); 109 String weightStr = parser.getAttributeValue(null, "weight"); 110 int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); 111 boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); 112 String fallbackFor = parser.getAttributeValue(null, "fallbackFor"); 113 StringBuilder filename = new StringBuilder(); 114 while (parser.next() != XmlPullParser.END_TAG) { 115 if (parser.getEventType() == XmlPullParser.TEXT) { 116 filename.append(parser.getText()); 117 } 118 if (parser.getEventType() != XmlPullParser.START_TAG) continue; 119 String tag = parser.getName(); 120 if (tag.equals("axis")) { 121 axes.add(readAxis(parser)); 122 } else { 123 skip(parser); 124 } 125 } 126 String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); 127 return new FontConfig.Font(sanitizedName, index, 128 axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); 129 } 130 readAxis(XmlPullParser parser)131 private static FontVariationAxis readAxis(XmlPullParser parser) 132 throws XmlPullParserException, IOException { 133 String tagStr = parser.getAttributeValue(null, "tag"); 134 String styleValueStr = parser.getAttributeValue(null, "stylevalue"); 135 skip(parser); // axis tag is empty, ignore any contents and consume end tag 136 return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr)); 137 } 138 readAlias(XmlPullParser parser)139 private static FontConfig.Alias readAlias(XmlPullParser parser) 140 throws XmlPullParserException, IOException { 141 String name = parser.getAttributeValue(null, "name"); 142 String toName = parser.getAttributeValue(null, "to"); 143 String weightStr = parser.getAttributeValue(null, "weight"); 144 int weight; 145 if (weightStr == null) { 146 weight = 400; 147 } else { 148 weight = Integer.parseInt(weightStr); 149 } 150 skip(parser); // alias tag is empty, ignore any contents and consume end tag 151 return new FontConfig.Alias(name, toName, weight); 152 } 153 skip(XmlPullParser parser)154 private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { 155 int depth = 1; 156 while (depth > 0) { 157 switch (parser.next()) { 158 case XmlPullParser.START_TAG: 159 depth++; 160 break; 161 case XmlPullParser.END_TAG: 162 depth--; 163 break; 164 } 165 } 166 } 167 } 168