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