OSDN Git Service

hotspot2: update Passpoint intent definitions am: 5679145d9a am: c4d5850d3e
[android-x86/frameworks-base.git] / graphics / java / android / graphics / FontListParser.java
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.util.Xml;
21
22 import org.xmlpull.v1.XmlPullParser;
23 import org.xmlpull.v1.XmlPullParserException;
24
25 import android.annotation.Nullable;
26 import com.android.internal.annotations.VisibleForTesting;
27
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.regex.Pattern;
33
34 /**
35  * Parser for font config files.
36  *
37  * @hide
38  */
39 public class FontListParser {
40
41     /* Parse fallback list (no names) */
42     public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
43         try {
44             XmlPullParser parser = Xml.newPullParser();
45             parser.setInput(in, null);
46             parser.nextTag();
47             return readFamilies(parser);
48         } finally {
49             in.close();
50         }
51     }
52
53     // Note that a well-formed variation contains a four-character tag and a float as styleValue,
54     // with spacers in between. The tag is enclosd either by double quotes or single quotes.
55     @VisibleForTesting
56     public static ArrayList<FontConfig.Axis> parseFontVariationSettings(@Nullable String settings) {
57         ArrayList<FontConfig.Axis> axisList = new ArrayList<>();
58         if (settings == null) {
59             return axisList;
60         }
61         String[] settingList = settings.split(",");
62         settingLoop:
63         for (String setting : settingList) {
64             int pos = 0;
65             while (pos < setting.length()) {
66                 char c = setting.charAt(pos);
67                 if (c == '\'' || c == '"') {
68                     break;
69                 } else if (!isSpacer(c)) {
70                     continue settingLoop;  // Only spacers are allowed before tag appeared.
71                 }
72                 pos++;
73             }
74             if (pos + 7 > setting.length()) {
75                 continue;  // 7 is the minimum length of tag-style value pair text.
76             }
77             if (setting.charAt(pos) != setting.charAt(pos + 5)) {
78                 continue;  // Tag should be wrapped with double or single quote.
79             }
80             String tagString = setting.substring(pos + 1, pos + 5);
81             if (!TAG_PATTERN.matcher(tagString).matches()) {
82                 continue;  // Skip incorrect format tag.
83             }
84             pos += 6;
85             while (pos < setting.length()) {
86                 if (!isSpacer(setting.charAt(pos++))) {
87                     break;  // Skip spacers between the tag and the styleValue.
88                 }
89             }
90             // Skip invalid styleValue
91             float styleValue;
92             String valueString = setting.substring(pos - 1);
93             if (!STYLE_VALUE_PATTERN.matcher(valueString).matches()) {
94                 continue;  // Skip incorrect format styleValue.
95             }
96             try {
97                 styleValue = Float.parseFloat(valueString);
98             } catch (NumberFormatException e) {
99                 continue;  // ignoreing invalid number format
100             }
101             int tag = makeTag(tagString.charAt(0), tagString.charAt(1), tagString.charAt(2),
102                     tagString.charAt(3));
103             axisList.add(new FontConfig.Axis(tag, styleValue));
104         }
105         return axisList;
106     }
107
108     @VisibleForTesting
109     public static int makeTag(char c1, char c2, char c3, char c4) {
110         return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
111     }
112
113     private static boolean isSpacer(char c) {
114         return c == ' ' || c == '\r' || c == '\t' || c == '\n';
115     }
116
117     private static FontConfig readFamilies(XmlPullParser parser)
118             throws XmlPullParserException, IOException {
119         FontConfig config = new FontConfig();
120         parser.require(XmlPullParser.START_TAG, null, "familyset");
121         while (parser.next() != XmlPullParser.END_TAG) {
122             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
123             String tag = parser.getName();
124             if (tag.equals("family")) {
125                 config.getFamilies().add(readFamily(parser));
126             } else if (tag.equals("alias")) {
127                 config.getAliases().add(readAlias(parser));
128             } else {
129                 skip(parser);
130             }
131         }
132         return config;
133     }
134
135     private static FontConfig.Family readFamily(XmlPullParser parser)
136             throws XmlPullParserException, IOException {
137         String name = parser.getAttributeValue(null, "name");
138         String lang = parser.getAttributeValue(null, "lang");
139         String variant = parser.getAttributeValue(null, "variant");
140         List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
141         while (parser.next() != XmlPullParser.END_TAG) {
142             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
143             String tag = parser.getName();
144             if (tag.equals("font")) {
145                 fonts.add(readFont(parser));
146             } else {
147                 skip(parser);
148             }
149         }
150         return new FontConfig.Family(name, fonts, lang, variant);
151     }
152
153     /** Matches leading and trailing XML whitespace. */
154     private static final Pattern FILENAME_WHITESPACE_PATTERN =
155             Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
156
157     private static FontConfig.Font readFont(XmlPullParser parser)
158             throws XmlPullParserException, IOException {
159         String indexStr = parser.getAttributeValue(null, "index");
160         int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
161         List<FontConfig.Axis> axes = new ArrayList<FontConfig.Axis>();
162         String weightStr = parser.getAttributeValue(null, "weight");
163         int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
164         boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
165         StringBuilder filename = new StringBuilder();
166         while (parser.next() != XmlPullParser.END_TAG) {
167             if (parser.getEventType() == XmlPullParser.TEXT) {
168                 filename.append(parser.getText());
169             }
170             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
171             String tag = parser.getName();
172             if (tag.equals("axis")) {
173                 axes.add(readAxis(parser));
174             } else {
175                 skip(parser);
176             }
177         }
178         String fullFilename = "/system/fonts/" +
179                 FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
180         return new FontConfig.Font(fullFilename, index, axes, weight, isItalic);
181     }
182
183     /** The 'tag' attribute value is read as four character values between U+0020 and U+007E
184      *  inclusive.
185      */
186     private static final Pattern TAG_PATTERN = Pattern.compile("[\\x20-\\x7E]{4}");
187
188     /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
189      *  '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
190      */
191     private static final Pattern STYLE_VALUE_PATTERN =
192             Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))");
193
194     private static FontConfig.Axis readAxis(XmlPullParser parser)
195             throws XmlPullParserException, IOException {
196         int tag = 0;
197         String tagStr = parser.getAttributeValue(null, "tag");
198         if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) {
199             tag = makeTag(tagStr.charAt(0), tagStr.charAt(1), tagStr.charAt(2), tagStr.charAt(3));
200         } else {
201             throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
202         }
203
204         float styleValue = 0;
205         String styleValueStr = parser.getAttributeValue(null, "stylevalue");
206         if (styleValueStr != null && STYLE_VALUE_PATTERN.matcher(styleValueStr).matches()) {
207             styleValue = Float.parseFloat(styleValueStr);
208         } else {
209             throw new XmlPullParserException("Invalid styleValue attribute value.", parser, null);
210         }
211
212         skip(parser);  // axis tag is empty, ignore any contents and consume end tag
213         return new FontConfig.Axis(tag, styleValue);
214     }
215
216     private static FontConfig.Alias readAlias(XmlPullParser parser)
217             throws XmlPullParserException, IOException {
218         String name = parser.getAttributeValue(null, "name");
219         String toName = parser.getAttributeValue(null, "to");
220         String weightStr = parser.getAttributeValue(null, "weight");
221         int weight;
222         if (weightStr == null) {
223             weight = 400;
224         } else {
225             weight = Integer.parseInt(weightStr);
226         }
227         skip(parser);  // alias tag is empty, ignore any contents and consume end tag
228         return new FontConfig.Alias(name, toName, weight);
229     }
230
231     private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
232         int depth = 1;
233         while (depth > 0) {
234             switch (parser.next()) {
235             case XmlPullParser.START_TAG:
236                 depth++;
237                 break;
238             case XmlPullParser.END_TAG:
239                 depth--;
240                 break;
241             }
242         }
243     }
244 }