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