diff options
Diffstat (limited to 'graphics')
7 files changed, 838 insertions, 11 deletions
diff --git a/graphics/java/android/graphics/FontListConverter.java b/graphics/java/android/graphics/FontListConverter.java new file mode 100644 index 0000000..c675c88 --- /dev/null +++ b/graphics/java/android/graphics/FontListConverter.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.graphics; + +import android.graphics.FontListParser; +import android.graphics.FontListParser.Alias; +import android.graphics.FontListParser.Font; +import android.graphics.LegacyFontListParser.Family; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +/** + * Converts a list of Family to FontListParser.Config + * {@hide} + */ +public class FontListConverter { + // These array values were determined by the order + // of fonts in a fileset. The order is: + // "Normal, Bold, Italic, BoldItalic" + // Additionally the weight values in L's fonts.xml + // are used to determine a generic weight value for each type + // e.g The 2nd entry in a fileset is the bold font. + protected static final int[] WEIGHTS = { 400, 700, 400, 700 }; + protected static boolean[] ITALICS = { false, false, true, true }; + protected static final int DEFAULT_WEIGHT = WEIGHTS[0]; + + // Arbitrarily chosen list based on L's fonts.xml. + // There could be more out there, but we can't use a generic pattern of "fontName-styleName" + // as "sans-serif" would be translated as a font called "sans" with the style "serif". + public static final String[] STYLES = { + "thin", + "light", + "medium", + "black" + }; + + // Maps a "normal" family to a list of similar families differing by style + // Example: "sans-serif" -> { sans-serif-thin, sans-serif-light, sans-serif-medium } + private HashMap<Family, List<Family>> mFamilyVariants = new HashMap<Family, List<Family>>(); + private List<Family> mFamilies = + new ArrayList<Family >(); + private String mFontDir; + + + public FontListConverter(List<Family> families, String fontDir) { + mFamilies.addAll(families); + mFontDir = fontDir; + findFamilyVariants(); + } + + public FontListConverter(Family family, String fontDir) { + mFamilies.add(family); + mFontDir = fontDir; + findFamilyVariants(); + } + + private void findFamilyVariants() { + for(Family family : mFamilies) { + if (isNormalStyle(family)) { + List<Family> variants = findVariants(family, mFamilies); + mFamilyVariants.put(family, variants); + } + } + } + + private List<Family> findVariants(Family normalFamily, List<Family> legacyFamilies) { + List<Family> variants = new ArrayList<Family>(); + + String normalFamilyName = normalFamily.getName(); + + for(Family family : legacyFamilies) { + String name = family.getName(); + + if (name.startsWith(normalFamilyName) && !isNormalStyle(family)) { + variants.add(family); + } + } + return variants; + } + + public FontListParser.Config convert() { + FontListParser.Config config = new FontListParser.Config(); + config.families.addAll(convertFamilies()); + config.aliases.addAll(createAliases()); + return config; + } + + /** + * A "normal" style is just standard font, + * eg Roboto is normal. Roboto-Thin is styled. + */ + protected boolean isNormalStyle(Family family) { + String name = family.getName(); + if (name == null) return false; + + for(String style : STYLES) { + if (name.endsWith('-' + style)) { + return false; + } + } + return true; + } + + protected List<FontListParser.Family> convertFamilies() { + List<FontListParser.Family> convertedFamilies = new ArrayList<FontListParser.Family>(); + + // Only convert normal families. Each normal family will add in its variants + for(Family family : mFamilyVariants.keySet()) { + convertedFamilies.add(convertFamily(family)); + } + + return convertedFamilies; + } + + protected FontListParser.Family convertFamily(Family legacyFamily) { + List<String> nameset = legacyFamily.nameset; + List<String> fileset = legacyFamily.fileset; + + // Arbitrarily choose the first entry in the nameset to be the name + String name = nameset.isEmpty() ? null : nameset.get(0); + + List<Font> fonts = convertFonts(fileset); + + // Add fonts from other variants + for(Family variantFamily : mFamilyVariants.get(legacyFamily)) { + fonts.addAll(convertFonts(variantFamily.fileset)); + } + + return new FontListParser.Family(name, fonts, null, null); + } + + protected List<FontListParser.Font> convertFonts(List<String> fileset) { + List<Font> fonts = new ArrayList<Font>(); + + for(int i=0; i < fileset.size(); i++) { + String fullpath = mFontDir + File.separatorChar + fileset.get(i); + // fileset should only be 4 entries, but if + // its more we can just assign a default. + int weight = i < WEIGHTS.length ? WEIGHTS[i] : DEFAULT_WEIGHT; + boolean isItalic = i < ITALICS.length ? ITALICS[i] : false; + + Font font = new Font(fullpath, weight, isItalic); + fonts.add(font); + } + + return fonts; + } + + protected List<Alias> createAliases() { + List<Alias> aliases = new ArrayList<Alias>(); + + for(Family family : mFamilyVariants.keySet()) { + // Get any aliases that might be from a normal family's nameset. + // eg sans-serif, arial, helvetica, tahoma etc. + if (isNormalStyle(family)) { + aliases.addAll(adaptNamesetAliases(family.nameset)); + } + } + + aliases.addAll(getAliasesForRelatedFamilies()); + + return aliases; + } + + private List<Alias> getAliasesForRelatedFamilies() { + List<Alias> aliases = new ArrayList<Alias>(); + + for(Entry<Family, List<Family>> entry : mFamilyVariants.entrySet()) { + String toName = entry.getKey().nameset.get(0); + List<Family> relatedFamilies = entry.getValue(); + for(Family relatedFamily : relatedFamilies) { + aliases.addAll(adaptNamesetAliases(relatedFamily.nameset, toName)); + } + } + return aliases; + } + + private List<Alias> adaptNamesetAliases(List<String> nameset, String toName) { + List<Alias> aliases = new ArrayList<Alias>(); + for(String name : nameset) { + Alias alias = new Alias(); + alias.name = name; + alias.toName = toName; + aliases.add(alias); + } + return aliases; + } + + private List<Alias> adaptNamesetAliases(List<String> nameset) { + List<Alias> aliases = new ArrayList<Alias>(); + if (nameset.size() < 2) return aliases; // An alias requires a name and toName + + String toName = nameset.get(0); + for(int i = 1; i < nameset.size(); i++) { + Alias alias = new Alias(); + alias.name = nameset.get(i); + alias.toName = toName; + aliases.add(alias); + } + + return aliases; + } +} diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 97081f9..1ca464d 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -21,6 +21,8 @@ import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -74,25 +76,64 @@ public class FontListParser { } /* Parse fallback list (no names) */ - public static Config parse(InputStream in) throws XmlPullParserException, IOException { + public static Config parse(File configFilename, File fontDir) + throws XmlPullParserException, IOException { + FileInputStream in = new FileInputStream(configFilename); + if (isLegacyFormat(configFilename)) { + return parseLegacyFormat(in, fontDir.getAbsolutePath()); + } else { + return parseNormalFormat(in, fontDir.getAbsolutePath()); + } + } + + private static boolean isLegacyFormat(File configFilename) + throws XmlPullParserException, IOException { + FileInputStream in = new FileInputStream(configFilename); + boolean isLegacy = false; + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + parser.require(XmlPullParser.START_TAG, null, "familyset"); + String version = parser.getAttributeValue(null, "version"); + isLegacy = version == null; + } finally { + in.close(); + } + return isLegacy; + } + + public static Config parseLegacyFormat(InputStream in, String dirName) + throws XmlPullParserException, IOException { + try { + List<LegacyFontListParser.Family> legacyFamilies = LegacyFontListParser.parse(in); + FontListConverter converter = new FontListConverter(legacyFamilies, dirName); + return converter.convert(); + } finally { + in.close(); + } + } + + public static Config parseNormalFormat(InputStream in, String dirName) + throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser); + return readFamilies(parser, dirName); } finally { in.close(); } } - private static Config readFamilies(XmlPullParser parser) + private static Config readFamilies(XmlPullParser parser, String dirPath) throws XmlPullParserException, IOException { Config config = new Config(); parser.require(XmlPullParser.START_TAG, null, "familyset"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; if (parser.getName().equals("family")) { - config.families.add(readFamily(parser)); + config.families.add(readFamily(parser, dirPath)); } else if (parser.getName().equals("alias")) { config.aliases.add(readAlias(parser)); } else { @@ -102,7 +143,7 @@ public class FontListParser { return config; } - private static Family readFamily(XmlPullParser parser) + private static Family readFamily(XmlPullParser parser, String dirPath) throws XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); String lang = parser.getAttributeValue(null, "lang"); @@ -116,7 +157,7 @@ public class FontListParser { int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); String filename = parser.nextText(); - String fullFilename = "/system/fonts/" + filename; + String fullFilename = dirPath + File.separatorChar + filename; fonts.add(new Font(fullFilename, weight, isItalic)); } else { skip(parser); diff --git a/graphics/java/android/graphics/LegacyFontListParser.java b/graphics/java/android/graphics/LegacyFontListParser.java new file mode 100644 index 0000000..adb37a3 --- /dev/null +++ b/graphics/java/android/graphics/LegacyFontListParser.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.graphics; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Parses an XML font config. Example: + * + *<familyset> + * + * <family> + * <nameset> + * <name>sans-serif</name> + * <name>arial</name> + * </nameset> + * <fileset> + * <file>Roboto-Regular.ttf</file> + * <file>Roboto-Bold.ttf</file> + * <file>Roboto-Italic.ttf</file> + * <file>Roboto-BoldItalic.ttf</file> + * </fileset> + * </family> + * <family> + * ... + * </family> + *</familyset> + */ +public class LegacyFontListParser { + public static class Family { + public List<String> nameset = new ArrayList<String>(); + public List<String> fileset = new ArrayList<String>(); + + public String getName() { + if (nameset != null && !nameset.isEmpty()) { + return nameset.get(0); + } + return null; + } + } + + public static List<Family> parse(InputStream in) + throws XmlPullParserException, IOException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilySet(parser); + } finally { + in.close(); + } + } + + private static List<Family> readFamilySet(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<Family> families = new ArrayList<Family>(); + parser.require(XmlPullParser.START_TAG, null, "familyset"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + + // Starts by looking for the entry tag + if (name.equals("family")) { + Family family = readFamily(parser); + families.add(family); + } + } + return families; + } + + private static Family readFamily(XmlPullParser parser) + throws XmlPullParserException, IOException { + Family family = new Family(); + parser.require(XmlPullParser.START_TAG, null, "family"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("nameset")) { + List<String> nameset = readNameset(parser); + family.nameset = nameset; + } else if (name.equals("fileset")) { + List<String> fileset = readFileset(parser); + family.fileset = fileset; + } else { + skip(parser); + } + } + return family; + } + + private static List<String> readNameset(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<String> names = new ArrayList<String>(); + parser.require(XmlPullParser.START_TAG, null, "nameset"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String tagname = parser.getName(); + if (tagname.equals("name")) { + String name = readText(parser); + names.add(name); + } else { + skip(parser); + } + } + return names; + } + + private static List<String> readFileset(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<String> files = new ArrayList<String>(); + parser.require(XmlPullParser.START_TAG, null, "fileset"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("file")) { + String file = readText(parser); + files.add(file); + } else { + skip(parser); + } + } + return files; + } + + // For the tags title and summary, extracts their text values. + private static String readText(XmlPullParser parser) + throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } + + private static void skip(XmlPullParser parser) + throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } +}
\ No newline at end of file diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index db42314..5c62a86 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -25,7 +25,6 @@ import android.util.SparseArray; import org.xmlpull.v1.XmlPullParserException; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; @@ -228,7 +227,10 @@ public class Typeface { for (int i = 0; i < families.length; i++) { ptrArray[i] = families[i].mNativePtr; } - return new Typeface(nativeCreateFromArray(ptrArray)); + + + Typeface typeface = new Typeface(nativeCreateFromArray(ptrArray)); + return typeface; } /** @@ -275,10 +277,24 @@ public class Typeface { private static void init() { // Load font config and initialize Minikin state File systemFontConfigLocation = getSystemFontConfigLocation(); - File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG); + File themeFontConfigLocation = getThemeFontConfigLocation(); + + File systemConfigFilename = new File(systemFontConfigLocation, FONTS_CONFIG); + File themeConfigFilename = new File(themeFontConfigLocation, FONTS_CONFIG); + File configFilename = null; + File fontDir; + + + if (themeConfigFilename.exists()) { + configFilename = themeConfigFilename; + fontDir = getThemeFontConfigLocation(); + } else { + configFilename = systemConfigFilename; + fontDir = getSystemFontDirLocation(); + } + try { - FileInputStream fontsIn = new FileInputStream(configFilename); - FontListParser.Config fontConfig = FontListParser.parse(fontsIn); + FontListParser.Config fontConfig = FontListParser.parse(configFilename, fontDir); List<FontFamily> familyList = new ArrayList<FontFamily>(); // Note that the default typeface is always present in the fallback list; @@ -332,6 +348,33 @@ public class Typeface { } } + /** + * Clears caches in java and skia. + * Skia will then reparse font config + * @hide + */ + public static void recreateDefaults() { + sTypefaceCache.clear(); + sSystemFontMap.clear(); + init(); + + long newDefault = create((String) null, 0).native_instance; + long newDefaultBold = create((String) null, Typeface.BOLD).native_instance; + long newSansSerif = create("sans-serif", 0).native_instance; + long newSerif = create("serif", 0).native_instance; + long newMonoSpace = create("monospace", 0).native_instance; + long newItalic = create((String) null, Typeface.ITALIC).native_instance; + long newBoldItalic = create((String) null, Typeface.BOLD_ITALIC).native_instance; + + DEFAULT.native_instance = newDefault; + DEFAULT_BOLD.native_instance = newDefaultBold; + SANS_SERIF.native_instance = newSansSerif; + SERIF.native_instance = newSerif; + MONOSPACE.native_instance = newMonoSpace; + sDefaults[2].native_instance = newItalic; + sDefaults[3].native_instance = newBoldItalic; + } + static { init(); // Set up defaults and typefaces exposed in public API @@ -354,6 +397,18 @@ public class Typeface { return new File("/system/etc/"); } + private static File getSystemFontDirLocation() { + return new File("/system/fonts/"); + } + + private static File getThemeFontConfigLocation() { + return new File("/data/system/theme/fonts/"); + } + + private static File getThemeFontDir() { + return new File("/data/system/theme/fonts/"); + } + @Override protected void finalize() throws Throwable { try { diff --git a/graphics/tests/localtests/Android.mk b/graphics/tests/localtests/Android.mk new file mode 100644 index 0000000..0b95ecd --- /dev/null +++ b/graphics/tests/localtests/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := FrameworksGraphicsHostTests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_JAVA_LIBRARIES := android.test.runner +#LOCAL_PACKAGE_NAME := FrameworksGraphicsTests + +include $(BUILD_STATIC_JAVA_LIBRARY) + +##################################### + + diff --git a/graphics/tests/localtests/src/android/graphics/FontListConverterTest.java b/graphics/tests/localtests/src/android/graphics/FontListConverterTest.java new file mode 100644 index 0000000..60c9053 --- /dev/null +++ b/graphics/tests/localtests/src/android/graphics/FontListConverterTest.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.graphics; + +import android.graphics.FontListParser.Alias; +import android.graphics.LegacyFontListParser.Family; +import android.test.suitebuilder.annotation.SmallTest; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; + +public class FontListConverterTest extends TestCase { + // VALID nameset includes the default name first and + // and some other 'aliases' with it. + private static final String[] VALID_NAMESET = { + "sans-serif", + "arial", + "helvetica", + "tahoma", + "verdana" + }; + + // The correct fileset will have 4 files in + // order by type (regular, bold, italic, bolditalic) + private static final String[] VALID_FILESET = { + "Roboto-Regular.ttf", + "Roboto-Bold.ttf", + "Roboto-Italic.ttf", + "Roboto-BoldItalic.ttf" + }; + + // The legacy fontlist format considered thin, light, and black styles + // each as part of their own familysets. The new format does not, so we need + // to provide a test case to adapt this. Note: "condensed" is still considered + // to be its own familyset. So we must be careful + private static final String[] VALID_ADDITIONAL_STYLE_NAMESET = { + "sans-serif-thin" + }; + + private static final String[] VALID_ADDITIONAL_STYLE_FILESET = { + "Roboto-Thin.ttf", + "Roboto-ThinItalic.ttf" + }; + + // thin, light, and black styles are part of the same family but a Roboto "condensed" + // or Roboto "slab" would be considered part of a different family. Since the legacy + // format would already consider these as a different family, we just have to make sure + // they don't get brought back into a common family like thin/light/black + private static final String[] VALID_RELATED_FAMILY_NAMESET = { + "sans-serif-condensed" + }; + + private static final String[] VALID_RELATED_FAMILY_FILESET = { + "RobotoCondensed-Regular.ttf", + "RobotoCondensed-Bold.ttf", + "RobotoCondensed-Italic.ttf", + "RobotoCondensed-BoldItalic.ttf" + }; + + // Some typefaces will only have one style. + private static final String[] VALID_SINGLE_STYLE_FAMIlY_NAMESET = { + "monospace" + }; + private static final String[] VALID_SINGLE_STYLE_FAMIlY_FILESET = { + "DroidSansMono.ttf" + }; + + final String VALID_PATH = "/valid/path/"; + + private Family sValidFamily; // eg "sans-serif" + private Family sValidAdditionalStyleFamily; // eg "sans-serif-light" + private Family sValidRelatedFamily; // eg "sans-serif-condensed" + private Family mValidSingleStyleFamily; // eg "monospace" which only uses DroidSansMono.ttf + + protected void setUp() { + sValidFamily = new Family(); + sValidFamily.nameset = new ArrayList<String>(Arrays.asList(VALID_NAMESET)); + sValidFamily.fileset = new ArrayList<String>(Arrays.asList(VALID_FILESET)); + + sValidAdditionalStyleFamily = new Family(); + sValidAdditionalStyleFamily.nameset = + new ArrayList<String>(Arrays.asList(VALID_ADDITIONAL_STYLE_NAMESET)); + sValidAdditionalStyleFamily.fileset = + new ArrayList<String>(Arrays.asList(VALID_ADDITIONAL_STYLE_FILESET)); + + sValidRelatedFamily = new Family(); + sValidRelatedFamily.nameset = + new ArrayList<String>(Arrays.asList(VALID_RELATED_FAMILY_NAMESET)); + sValidRelatedFamily.fileset = + new ArrayList<String>(Arrays.asList(VALID_RELATED_FAMILY_FILESET)); + + mValidSingleStyleFamily = new Family(); + } + + @SmallTest + public void testValidAdaptedFamilyShouldHaveNameOfNamesetsFirstElement() { + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + FontListParser.Family convertedFamily = adapter.convertFamily(sValidFamily); + assertEquals(VALID_NAMESET[0], convertedFamily.name); + } + + @SmallTest + public void testValidAdaptedFamilyShouldHaveFonts() { + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + FontListParser.Family convertedFamily = adapter.convertFamily(sValidFamily); + List<FontListParser.Font> fonts = convertedFamily.fonts; + assertEquals(VALID_FILESET.length, fonts.size()); + } + + @SmallTest + public void testValidAdaptedFontsShouldHaveCorrectProperties() { + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + List<FontListParser.Font> fonts = adapter.convertFonts(Arrays.asList(VALID_FILESET)); + + assertEquals(VALID_FILESET.length, fonts.size()); + for(int i=0; i < fonts.size(); i++) { + FontListParser.Font font = fonts.get(i); + assertEquals(VALID_PATH + VALID_FILESET[i], font.fontName); + assertEquals("shouldBeItalic", shouldBeItalic(i), font.isItalic); + assertEquals(FontListConverter.WEIGHTS[i], font.weight); + } + } + + @SmallTest + public void testExtraNamesetsShouldConvertToAliases() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); + + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + List<FontListParser.Alias> aliases = adapter.createAliases(); + + // Be sure the aliases point to the first name in the nameset + for(int i = 0; i < aliases.size(); i++) { + FontListParser.Alias alias = aliases.get(i); + assertEquals(VALID_NAMESET[0], alias.toName); + } + + // Be sure the extra namesets are in the alias list + for(int i = 1; i < VALID_NAMESET.length; i++) { + assertTrue("hasAliasWithName", hasAliasWithName(aliases, VALID_NAMESET[i])); + } + } + + /** + * The legacy format treats thin, light, and black fonts to be different families + * The new format treats these as all part of the original + * eg sans-serif and sans-serif-thin become one family + */ + @SmallTest + public void testAdditionalStylesShouldConvertToSameFamily() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); //eg "sans-serif" + families.add(sValidAdditionalStyleFamily); //eg "sans-serif-light" + + FontListConverter adapter = new FontListConverter(families, VALID_PATH); + List<FontListParser.Family> convertedFamilies = adapter.convertFamilies(); + + // We started with two similiar families, and now should have one + assertEquals(1, convertedFamilies.size()); + + // The name of the family should be the base name, no style modifiers + // ie "sans-serif" not "sans-serif-light" + FontListParser.Family convertedFamily = convertedFamilies.get(0); + assertEquals(sValidFamily.nameset.get(0), convertedFamily.name); + + // Verify all the fonts from both families exist now in the converted Family + List<String> combinedFileSet = new ArrayList<String>(); + combinedFileSet.addAll(sValidFamily.fileset); + combinedFileSet.addAll(sValidAdditionalStyleFamily.fileset); + for(String filename : combinedFileSet) { + String fontName = VALID_PATH + filename; + assertTrue("hasFontWithName", hasFontWithName(convertedFamily, fontName)); + } + } + + /** + * When two families combine, the "varied" family (ie light, light, black) should + * have their namesets converted to aliases. + * IE sans-serif-light should point to sans-serif because the light family + * gets merged to sans-serif + */ + @SmallTest + public void testAdditionalStylesNamesShouldBecomeAliases() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); //eg "sans-serif" + families.add(sValidAdditionalStyleFamily); //eg "sans-serif-light" + + FontListConverter adapter = new FontListConverter(families, VALID_PATH); + List<Alias> aliases = adapter.createAliases(); + + // Subtract 1 from the total length since VALID_NAMESET[0] will be the family name + int expectedSize = VALID_NAMESET.length + VALID_ADDITIONAL_STYLE_NAMESET.length - 1; + assertEquals(expectedSize, aliases.size()); + + // All aliases should point at the base family + for(Alias alias : aliases) { + assertEquals(VALID_NAMESET[0], alias.toName); + } + + // There should be an alias for every name in the merged in family + for(String name : VALID_ADDITIONAL_STYLE_NAMESET) { + assertTrue("hasAliasWithName", hasAliasWithName(aliases, name)); + } + } + + /** + * sans-serif-condensed should not get merged in with sans-serif + */ + @SmallTest + public void testSimiliarFontsShouldKeepSameFamily() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); //eg "sans-serif" + families.add(sValidRelatedFamily); //eg "sans-serif-condensed" + + FontListConverter adapter = new FontListConverter(families, VALID_PATH); + List<FontListParser.Family> convertedFamilies = adapter.convertFamilies(); + FontListParser.Family convertedValidFamily = + getFontListFamilyWithName(convertedFamilies, VALID_NAMESET[0]); + FontListParser.Family convertedRelatedFamily = + getFontListFamilyWithName(convertedFamilies, VALID_RELATED_FAMILY_NAMESET[0]); + + + // Valid family should only have its own fonts. Will fail if these were merged + for(String filename : sValidFamily.fileset) { + String fontName = VALID_PATH + filename; + assertTrue("hasFontWithName", hasFontWithName(convertedValidFamily, fontName)); + assertFalse("hasFontWIthName", hasFontWithName(convertedRelatedFamily, fontName)); + } + + // Related family should also only have have its own fonts. Will fail if these were merged + for(String filename : sValidRelatedFamily.fileset) { + String fontName = VALID_PATH + filename; + assertTrue("hasFontWithName", hasFontWithName(convertedRelatedFamily, fontName)); + assertFalse("hasFontWIthName", hasFontWithName(convertedValidFamily, fontName)); + } + } + + private static boolean hasAliasWithName(List<Alias> aliases, String name) { + for (Alias alias : aliases) if (name.equals(alias.name)) return true; + return false; + } + + private static boolean hasFontWithName(FontListParser.Family family, String name) { + for (FontListParser.Font font : family.fonts) { + if(font.fontName != null && font.fontName.equals(name)) { + return true; + } + } + return false; + } + + private static FontListParser.Family getFontListFamilyWithName( + List<FontListParser.Family> families, String name) { + for(FontListParser.Family family : families) { + if (name.equals(family.name)) return family; + } + return null; + } + + private boolean shouldBeItalic(int index) { + // Since the fileset format is regular, bold, italic, bolditalic, anything >= 2 is italic + return index >= 2; + } +} diff --git a/graphics/tests/localtests/src/android/graphics/TypefaceTestSuite.java b/graphics/tests/localtests/src/android/graphics/TypefaceTestSuite.java new file mode 100644 index 0000000..9ef8421 --- /dev/null +++ b/graphics/tests/localtests/src/android/graphics/TypefaceTestSuite.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008 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.graphics; + +import junit.framework.TestSuite; + +public class TypefaceTestSuite { + public static TestSuite suite() { + TestSuite suite = new TestSuite(TypefaceTestSuite.class.getName()); + suite.addTestSuite(FontListConverterTest.class); + return suite; + } +} |