• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package android.content.res;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.graphics.Typeface;
21 import android.util.AttributeSet;
22 import android.util.Log;
23 import android.util.Xml;
24 
25 import com.android.internal.R;
26 
27 import org.xmlpull.v1.XmlPullParser;
28 import org.xmlpull.v1.XmlPullParserException;
29 
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 
35 /**
36  * Parser for xml type font resources.
37  * @hide
38  */
39 public class FontResourcesParser {
40     private static final String TAG = "FontResourcesParser";
41 
42     // A class represents single entry of font-family in xml file.
43     public interface FamilyResourceEntry {}
44 
45     // A class represents font provider based font-family element in xml file.
46     public static final class ProviderResourceEntry implements FamilyResourceEntry {
47         private final @NonNull String mProviderAuthority;
48         private final @NonNull String mProviderPackage;
49         private final @NonNull String mQuery;
50         private final @Nullable String mSystemFontFamilyName;
51         private final @Nullable List<List<String>> mCerts;
52 
ProviderResourceEntry(@onNull String authority, @NonNull String pkg, @NonNull String query, @Nullable List<List<String>> certs, @Nullable String systemFontFamilyName)53         public ProviderResourceEntry(@NonNull String authority, @NonNull String pkg,
54                 @NonNull String query, @Nullable List<List<String>> certs,
55                 @Nullable String systemFontFamilyName) {
56             mProviderAuthority = authority;
57             mProviderPackage = pkg;
58             mQuery = query;
59             mCerts = certs;
60             mSystemFontFamilyName = systemFontFamilyName;
61         }
62 
getAuthority()63         public @NonNull String getAuthority() {
64             return mProviderAuthority;
65         }
66 
getPackage()67         public @NonNull String getPackage() {
68             return mProviderPackage;
69         }
70 
getQuery()71         public @NonNull String getQuery() {
72             return mQuery;
73         }
74 
getSystemFontFamilyName()75         public @NonNull String getSystemFontFamilyName() {
76             return mSystemFontFamilyName;
77         }
78 
getCerts()79         public @Nullable List<List<String>> getCerts() {
80             return mCerts;
81         }
82     }
83 
84     // A class represents font element in xml file which points a file in resource.
85     public static final class FontFileResourceEntry {
86         public static final int RESOLVE_BY_FONT_TABLE = Typeface.RESOLVE_BY_FONT_TABLE;
87         public static final int UPRIGHT = 0;
88         public static final int ITALIC = 1;
89 
90         private final @NonNull String mFileName;
91         private int mWeight;
92         private int mItalic;
93         private int mTtcIndex;
94         private String mVariationSettings;
95         private int mResourceId;
96 
FontFileResourceEntry(@onNull String fileName, int weight, int italic, @Nullable String variationSettings, int ttcIndex)97         public FontFileResourceEntry(@NonNull String fileName, int weight, int italic,
98                 @Nullable String variationSettings, int ttcIndex) {
99             mFileName = fileName;
100             mWeight = weight;
101             mItalic = italic;
102             mVariationSettings = variationSettings;
103             mTtcIndex = ttcIndex;
104         }
105 
getFileName()106         public @NonNull String getFileName() {
107             return mFileName;
108         }
109 
getWeight()110         public int getWeight() {
111             return mWeight;
112         }
113 
getItalic()114         public int getItalic() {
115             return mItalic;
116         }
117 
getVariationSettings()118         public @Nullable String getVariationSettings() {
119             return mVariationSettings;
120         }
121 
getTtcIndex()122         public int getTtcIndex() {
123             return mTtcIndex;
124         }
125     }
126 
127     // A class represents file based font-family element in xml file.
128     public static final class FontFamilyFilesResourceEntry implements FamilyResourceEntry {
129         private final @NonNull FontFileResourceEntry[] mEntries;
130 
FontFamilyFilesResourceEntry(@onNull FontFileResourceEntry[] entries)131         public FontFamilyFilesResourceEntry(@NonNull FontFileResourceEntry[] entries) {
132             mEntries = entries;
133         }
134 
getEntries()135         public @NonNull FontFileResourceEntry[] getEntries() {
136             return mEntries;
137         }
138     }
139 
parse(XmlPullParser parser, Resources resources)140     public static @Nullable FamilyResourceEntry parse(XmlPullParser parser, Resources resources)
141             throws XmlPullParserException, IOException {
142         int type;
143         while ((type=parser.next()) != XmlPullParser.START_TAG
144                 && type != XmlPullParser.END_DOCUMENT) {
145             // Empty loop.
146         }
147 
148         if (type != XmlPullParser.START_TAG) {
149             throw new XmlPullParserException("No start tag found");
150         }
151         return readFamilies(parser, resources);
152     }
153 
readFamilies(XmlPullParser parser, Resources resources)154     private static @Nullable FamilyResourceEntry readFamilies(XmlPullParser parser,
155             Resources resources) throws XmlPullParserException, IOException {
156         parser.require(XmlPullParser.START_TAG, null, "font-family");
157         String tag = parser.getName();
158         FamilyResourceEntry result = null;
159         if (tag.equals("font-family")) {
160             return readFamily(parser, resources);
161         } else {
162             skip(parser);
163             Log.e(TAG, "Failed to find font-family tag");
164             return null;
165         }
166     }
167 
readFamily(XmlPullParser parser, Resources resources)168     private static @Nullable FamilyResourceEntry readFamily(XmlPullParser parser,
169             Resources resources) throws XmlPullParserException, IOException {
170         AttributeSet attrs = Xml.asAttributeSet(parser);
171         TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
172         String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
173         String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
174         String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
175         int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0);
176         String systemFontFamilyName = array.getString(
177                 R.styleable.FontFamily_fontProviderSystemFontFamily);
178         array.recycle();
179         if (authority != null && providerPackage != null && query != null) {
180             while (parser.next() != XmlPullParser.END_TAG) {
181                 skip(parser);
182             }
183             List<List<String>> certs = null;
184             if (certsId != 0) {
185                 TypedArray typedArray = resources.obtainTypedArray(certsId);
186                 if (typedArray.length() > 0) {
187                     certs = new ArrayList<>();
188                     boolean isArrayOfArrays = typedArray.getResourceId(0, 0) != 0;
189                     if (isArrayOfArrays) {
190                         for (int i = 0; i < typedArray.length(); i++) {
191                             int certId = typedArray.getResourceId(i, 0);
192                             String[] certsArray = resources.getStringArray(certId);
193                             List<String> certsList = Arrays.asList(certsArray);
194                             certs.add(certsList);
195                         }
196                     } else {
197                         String[] certsArray = resources.getStringArray(certsId);
198                         List<String> certsList = Arrays.asList(certsArray);
199                         certs.add(certsList);
200                     }
201                 }
202             }
203             return new ProviderResourceEntry(
204                     authority,
205                     providerPackage,
206                     query,
207                     certs,
208                     systemFontFamilyName
209             );
210         }
211         List<FontFileResourceEntry> fonts = new ArrayList<>();
212         while (parser.next() != XmlPullParser.END_TAG) {
213             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
214             String tag = parser.getName();
215             if (tag.equals("font")) {
216                 final FontFileResourceEntry entry = readFont(parser, resources);
217                 if (entry != null) {
218                     fonts.add(entry);
219                 }
220             } else {
221                 skip(parser);
222             }
223         }
224         if (fonts.isEmpty()) {
225             return null;
226         }
227         return new FontFamilyFilesResourceEntry(fonts.toArray(
228                 new FontFileResourceEntry[fonts.size()]));
229     }
230 
readFont(XmlPullParser parser, Resources resources)231     private static FontFileResourceEntry readFont(XmlPullParser parser, Resources resources)
232             throws XmlPullParserException, IOException {
233         AttributeSet attrs = Xml.asAttributeSet(parser);
234         TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamilyFont);
235         int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight,
236                 Typeface.RESOLVE_BY_FONT_TABLE);
237         int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
238                 FontFileResourceEntry.RESOLVE_BY_FONT_TABLE);
239         String variationSettings = array.getString(
240                 R.styleable.FontFamilyFont_fontVariationSettings);
241         int ttcIndex = array.getInt(R.styleable.FontFamilyFont_ttcIndex, 0);
242         String filename = array.getString(R.styleable.FontFamilyFont_font);
243         array.recycle();
244         while (parser.next() != XmlPullParser.END_TAG) {
245             skip(parser);
246         }
247         if (filename == null) {
248             return null;
249         }
250         return new FontFileResourceEntry(filename, weight, italic, variationSettings, ttcIndex);
251     }
252 
skip(XmlPullParser parser)253     private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
254         int depth = 1;
255         while (depth > 0) {
256             switch (parser.next()) {
257                 case XmlPullParser.START_TAG:
258                     depth++;
259                     break;
260                 case XmlPullParser.END_TAG:
261                     depth--;
262                     break;
263             }
264         }
265     }
266 }
267