/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content.res; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Typeface; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Parser for xml type font resources. * @hide */ public class FontResourcesParser { private static final String TAG = "FontResourcesParser"; // A class represents single entry of font-family in xml file. public interface FamilyResourceEntry {} // A class represents font provider based font-family element in xml file. public static final class ProviderResourceEntry implements FamilyResourceEntry { private final @NonNull String mProviderAuthority; private final @NonNull String mProviderPackage; private final @NonNull String mQuery; private final @Nullable String mSystemFontFamilyName; private final @Nullable List> mCerts; public ProviderResourceEntry(@NonNull String authority, @NonNull String pkg, @NonNull String query, @Nullable List> certs, @Nullable String systemFontFamilyName) { mProviderAuthority = authority; mProviderPackage = pkg; mQuery = query; mCerts = certs; mSystemFontFamilyName = systemFontFamilyName; } public @NonNull String getAuthority() { return mProviderAuthority; } public @NonNull String getPackage() { return mProviderPackage; } public @NonNull String getQuery() { return mQuery; } public @NonNull String getSystemFontFamilyName() { return mSystemFontFamilyName; } public @Nullable List> getCerts() { return mCerts; } } // A class represents font element in xml file which points a file in resource. public static final class FontFileResourceEntry { public static final int RESOLVE_BY_FONT_TABLE = Typeface.RESOLVE_BY_FONT_TABLE; public static final int UPRIGHT = 0; public static final int ITALIC = 1; private final @NonNull String mFileName; private int mWeight; private int mItalic; private int mTtcIndex; private String mVariationSettings; private int mResourceId; public FontFileResourceEntry(@NonNull String fileName, int weight, int italic, @Nullable String variationSettings, int ttcIndex) { mFileName = fileName; mWeight = weight; mItalic = italic; mVariationSettings = variationSettings; mTtcIndex = ttcIndex; } public @NonNull String getFileName() { return mFileName; } public int getWeight() { return mWeight; } public int getItalic() { return mItalic; } public @Nullable String getVariationSettings() { return mVariationSettings; } public int getTtcIndex() { return mTtcIndex; } } // A class represents file based font-family element in xml file. public static final class FontFamilyFilesResourceEntry implements FamilyResourceEntry { private final @NonNull FontFileResourceEntry[] mEntries; public FontFamilyFilesResourceEntry(@NonNull FontFileResourceEntry[] entries) { mEntries = entries; } public @NonNull FontFileResourceEntry[] getEntries() { return mEntries; } } public static @Nullable FamilyResourceEntry parse(XmlPullParser parser, Resources resources) throws XmlPullParserException, IOException { int type; while ((type=parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty loop. } if (type != XmlPullParser.START_TAG) { throw new XmlPullParserException("No start tag found"); } return readFamilies(parser, resources); } private static @Nullable FamilyResourceEntry readFamilies(XmlPullParser parser, Resources resources) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, null, "font-family"); String tag = parser.getName(); FamilyResourceEntry result = null; if (tag.equals("font-family")) { return readFamily(parser, resources); } else { skip(parser); Log.e(TAG, "Failed to find font-family tag"); return null; } } private static @Nullable FamilyResourceEntry readFamily(XmlPullParser parser, Resources resources) throws XmlPullParserException, IOException { AttributeSet attrs = Xml.asAttributeSet(parser); TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily); String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority); String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage); String query = array.getString(R.styleable.FontFamily_fontProviderQuery); int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0); String systemFontFamilyName = array.getString( R.styleable.FontFamily_fontProviderSystemFontFamily); array.recycle(); if (authority != null && providerPackage != null && query != null) { while (parser.next() != XmlPullParser.END_TAG) { skip(parser); } List> certs = null; if (certsId != 0) { TypedArray typedArray = resources.obtainTypedArray(certsId); if (typedArray.length() > 0) { certs = new ArrayList<>(); boolean isArrayOfArrays = typedArray.getResourceId(0, 0) != 0; if (isArrayOfArrays) { for (int i = 0; i < typedArray.length(); i++) { int certId = typedArray.getResourceId(i, 0); String[] certsArray = resources.getStringArray(certId); List certsList = Arrays.asList(certsArray); certs.add(certsList); } } else { String[] certsArray = resources.getStringArray(certsId); List certsList = Arrays.asList(certsArray); certs.add(certsList); } } typedArray.recycle(); } return new ProviderResourceEntry( authority, providerPackage, query, certs, systemFontFamilyName ); } List fonts = new ArrayList<>(); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("font")) { final FontFileResourceEntry entry = readFont(parser, resources); if (entry != null) { fonts.add(entry); } } else { skip(parser); } } if (fonts.isEmpty()) { return null; } return new FontFamilyFilesResourceEntry(fonts.toArray( new FontFileResourceEntry[fonts.size()])); } private static FontFileResourceEntry readFont(XmlPullParser parser, Resources resources) throws XmlPullParserException, IOException { AttributeSet attrs = Xml.asAttributeSet(parser); TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamilyFont); int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight, Typeface.RESOLVE_BY_FONT_TABLE); int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle, FontFileResourceEntry.RESOLVE_BY_FONT_TABLE); String variationSettings = array.getString( R.styleable.FontFamilyFont_fontVariationSettings); int ttcIndex = array.getInt(R.styleable.FontFamilyFont_ttcIndex, 0); String filename = array.getString(R.styleable.FontFamilyFont_font); array.recycle(); while (parser.next() != XmlPullParser.END_TAG) { skip(parser); } if (filename == null) { return null; } return new FontFileResourceEntry(filename, weight, italic, variationSettings, ttcIndex); } private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { int depth = 1; while (depth > 0) { switch (parser.next()) { case XmlPullParser.START_TAG: depth++; break; case XmlPullParser.END_TAG: depth--; break; } } } }