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