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