diff options
-rw-r--r-- | core/jni/android/graphics/FontFamily.cpp | 4 | ||||
-rw-r--r-- | core/jni/android/graphics/Typeface.cpp | 6 | ||||
-rw-r--r-- | core/jni/android/graphics/TypefaceImpl.cpp | 52 | ||||
-rw-r--r-- | core/jni/android/graphics/TypefaceImpl.h | 2 | ||||
-rw-r--r-- | graphics/java/android/graphics/FontFamily.java | 8 | ||||
-rw-r--r-- | graphics/java/android/graphics/FontListParser.java | 117 | ||||
-rw-r--r-- | graphics/java/android/graphics/Typeface.java | 87 |
7 files changed, 248 insertions, 28 deletions
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 5782312..05154d9 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -49,6 +49,10 @@ static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, ScopedUtfChars str(env, path); ALOGD("addFont %s", str.c_str()); SkTypeface* face = SkTypeface::CreateFromFile(str.c_str()); + if (face == NULL) { + ALOGE("addFont failed to create font %s", str.c_str()); + return false; + } MinikinFont* minikinFont = new MinikinFontSkia(face); FontFamily* fontFamily = (FontFamily*)familyPtr; return fontFamily->addFont(minikinFont); diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index 02b04de..b20c246 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -120,6 +120,11 @@ static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArr return reinterpret_cast<jlong>(TypefaceImpl_createFromFamilies(families.get(), families.size())); } +static void Typeface_setDefault(JNIEnv *env, jobject, jlong faceHandle) { + TypefaceImpl* face = reinterpret_cast<TypefaceImpl*>(faceHandle); + return TypefaceImpl_setDefault(face); +} + /////////////////////////////////////////////////////////////////////////////// static JNINativeMethod gTypefaceMethods[] = { @@ -133,6 +138,7 @@ static JNINativeMethod gTypefaceMethods[] = { (void*)Typeface_createFromFile }, { "nativeCreateFromArray", "([J)J", (void*)Typeface_createFromArray }, + { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault }, }; int register_android_graphics_Typeface(JNIEnv* env) diff --git a/core/jni/android/graphics/TypefaceImpl.cpp b/core/jni/android/graphics/TypefaceImpl.cpp index a60dd7e..fa5acb8 100644 --- a/core/jni/android/graphics/TypefaceImpl.cpp +++ b/core/jni/android/graphics/TypefaceImpl.cpp @@ -52,48 +52,46 @@ static FontStyle styleFromSkiaStyle(SkTypeface::Style skiaStyle) { return FontStyle(weight, italic); } -TypefaceImpl* gDefaultTypeface; +TypefaceImpl* gDefaultTypeface = NULL; pthread_once_t gDefaultTypefaceOnce = PTHREAD_ONCE_INIT; -// TODO: this currently builds a font collection from hardcoded paths. -// It will get replaced by an implementation that parses the XML files. +// This installs a default typeface (from a hardcoded path) that allows +// layouts to work (not crash on null pointer) before the default +// typeface is set. +// TODO: investigate why layouts are being created before Typeface.java +// class initialization. static FontCollection *makeFontCollection() { std::vector<FontFamily *>typefaces; const char *fns[] = { "/system/fonts/Roboto-Regular.ttf", - "/system/fonts/Roboto-Italic.ttf", - "/system/fonts/Roboto-BoldItalic.ttf", - "/system/fonts/Roboto-Light.ttf", - "/system/fonts/Roboto-Thin.ttf", - "/system/fonts/Roboto-Bold.ttf", - "/system/fonts/Roboto-ThinItalic.ttf", - "/system/fonts/Roboto-LightItalic.ttf" }; FontFamily *family = new FontFamily(); for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) { const char *fn = fns[i]; + ALOGD("makeFontCollection adding %s", fn); SkTypeface *skFace = SkTypeface::CreateFromFile(fn); - MinikinFont *font = new MinikinFontSkia(skFace); - family->addFont(font); + if (skFace != NULL) { + MinikinFont *font = new MinikinFontSkia(skFace); + family->addFont(font); + } else { + ALOGE("failed to create font %s", fn); + } } typefaces.push_back(family); - family = new FontFamily(); - const char *fn = "/system/fonts/NotoSansDevanagari-Regular.ttf"; - SkTypeface *skFace = SkTypeface::CreateFromFile(fn); - MinikinFont *font = new MinikinFontSkia(skFace); - family->addFont(font); - typefaces.push_back(family); - return new FontCollection(typefaces); } static void getDefaultTypefaceOnce() { Layout::init(); - gDefaultTypeface = new TypefaceImpl; - gDefaultTypeface->fFontCollection = makeFontCollection(); - gDefaultTypeface->fStyle = FontStyle(); + if (gDefaultTypeface == NULL) { + // We expect the client to set a default typeface, but provide a + // default so we can make progress before that happens. + gDefaultTypeface = new TypefaceImpl; + gDefaultTypeface->fFontCollection = makeFontCollection(); + gDefaultTypeface->fStyle = FontStyle(); + } } TypefaceImpl* TypefaceImpl_resolveDefault(TypefaceImpl* src) { @@ -116,6 +114,9 @@ TypefaceImpl* TypefaceImpl_createFromTypeface(TypefaceImpl* src, SkTypeface::Sty } static TypefaceImpl* createFromSkTypeface(SkTypeface* typeface) { + if (typeface == NULL) { + return NULL; + } MinikinFont* minikinFont = new MinikinFontSkia(typeface); std::vector<FontFamily *> typefaces; FontFamily* family = new FontFamily(); @@ -176,6 +177,10 @@ int TypefaceImpl_getStyle(TypefaceImpl* face) { return result; } +void TypefaceImpl_setDefault(TypefaceImpl* face) { + gDefaultTypeface = face; +} + #else // USE_MINIKIN /* Just use SkTypeface instead. */ @@ -219,6 +224,9 @@ int TypefaceImpl_getStyle(TypefaceImpl* face) { return face->style(); } +void TypefaceImpl_setDefault(TypefaceImpl* face) { +} + #endif // USE_MINIKIN } diff --git a/core/jni/android/graphics/TypefaceImpl.h b/core/jni/android/graphics/TypefaceImpl.h index 4e021cd..4e92bce 100644 --- a/core/jni/android/graphics/TypefaceImpl.h +++ b/core/jni/android/graphics/TypefaceImpl.h @@ -61,6 +61,8 @@ void TypefaceImpl_unref(TypefaceImpl* face); int TypefaceImpl_getStyle(TypefaceImpl* face); +void TypefaceImpl_setDefault(TypefaceImpl* face); + } #endif // ANDROID_TYPEFACE_IMPL_H
\ No newline at end of file diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index afa8706..7c55ae8 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -24,9 +24,17 @@ import java.io.File; * @hide */ public class FontFamily { + /** + * @hide + */ public long mNativePtr; + public FontFamily() { mNativePtr = nCreateFamily(); + mNativePtr = nCreateFamily(); + if (mNativePtr == 0) { + throw new RuntimeException(); + } } // TODO: finalization diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java new file mode 100644 index 0000000..f304f4e --- /dev/null +++ b/graphics/java/android/graphics/FontListParser.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2014 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 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; + +/** + * Parser for font config files. + * + * @hide + */ +public class FontListParser { + + public static class Family { + public Family(List<String> names, List<String> fontFiles) { + this.names = names; + this.fontFiles = fontFiles; + } + + public List<String> names; + // todo: need attributes for font files + public List<String> fontFiles; + } + + /* Parse fallback list (no names) */ + public static List<Family> parse(InputStream in) throws XmlPullParserException, IOException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser); + } finally { + in.close(); + } + } + + private static List<Family> readFamilies(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; + if (parser.getName().equals("family")) { + families.add(readFamily(parser)); + } else { + skip(parser); + } + } + return families; + } + + private static Family readFamily(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<String> names = null; + List<String> fontFiles = new ArrayList<String>(); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + String tag = parser.getName(); + if (tag.equals("fileset")) { + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + if (parser.getName().equals("file")) { + String filename = parser.nextText(); + String fullFilename = "/system/fonts/" + filename; + fontFiles.add(fullFilename); + } + } + } else if (tag.equals("nameset")) { + names = new ArrayList<String>(); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + if (parser.getName().equals("name")) { + String name = parser.nextText(); + names.add(name); + } + } + } + } + return new Family(names, fontFiles); + } + + 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; + } + } + } +} diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 4b95fcd..7322948 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -17,11 +17,21 @@ package android.graphics; import android.content.res.AssetManager; +import android.graphics.FontListParser.Family; import android.util.Log; -import android.util.SparseArray; import android.util.LongSparseArray; +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; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * The Typeface class specifies the typeface and intrinsic style of a font. @@ -31,6 +41,8 @@ import java.io.File; */ public class Typeface { + private static String TAG = "Typeface"; + /** The default NORMAL typeface object */ public static final Typeface DEFAULT; /** @@ -50,6 +62,9 @@ public class Typeface { private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache = new LongSparseArray<SparseArray<Typeface>>(3); + static Typeface sDefaultTypeface; + static Map<String, Typeface> sSystemFontMap; + /** * @hide */ @@ -63,6 +78,11 @@ public class Typeface { private int mStyle = 0; + private static void setDefault(Typeface t) { + sDefaultTypeface = t; + nativeSetDefault(t.native_instance); + } + /** Returns the typeface's intrinsic style attributes */ public int getStyle() { return mStyle; @@ -90,6 +110,9 @@ public class Typeface { * @return The best matching typeface. */ public static Typeface create(String familyName, int style) { + if (sSystemFontMap != null) { + return create(sSystemFontMap.get(familyName), style); + } return new Typeface(nativeCreate(familyName, style)); } @@ -143,7 +166,7 @@ public class Typeface { public static Typeface defaultFromStyle(int style) { return sDefaults[style]; } - + /** * Create a new typeface from the specified font data. * @param mgr The application's asset manager @@ -157,7 +180,7 @@ public class Typeface { /** * Create a new typeface from the specified font file. * - * @param path The path to the font data. + * @param path The path to the font data. * @return The new typeface. */ public static Typeface createFromFile(File path) { @@ -167,7 +190,7 @@ public class Typeface { /** * Create a new typeface from the specified font file. * - * @param path The full path to the font data. + * @param path The full path to the font data. * @return The new typeface. */ public static Typeface createFromFile(String path) { @@ -199,14 +222,64 @@ public class Typeface { native_instance = ni; mStyle = nativeGetStyle(ni); } - + + private static FontFamily makeFamilyFromParsed(FontListParser.Family family) { + // TODO: expand to handle attributes like lang and variant + FontFamily fontFamily = new FontFamily(); + for (String fontFile : family.fontFiles) { + fontFamily.addFont(new File(fontFile)); + } + return fontFamily; + } + static { + // Load font config and initialize Minikin state + String systemConfigFilename = "/system/etc/system_fonts.xml"; + String configFilename = "/system/etc/fallback_fonts.xml"; + try { + // TODO: throws an exception non-Minikin builds, to fail early; + // remove when Minikin-only + new FontFamily(); + + FileInputStream systemIn = new FileInputStream(systemConfigFilename); + List<FontListParser.Family> systemFontConfig = FontListParser.parse(systemIn); + Map<String, Typeface> systemFonts = new HashMap<String, Typeface>(); + for (Family f : systemFontConfig) { + FontFamily fontFamily = makeFamilyFromParsed(f); + FontFamily[] families = { fontFamily }; + Typeface typeface = Typeface.createFromFamilies(families); + for (String name : f.names) { + systemFonts.put(name, typeface); + } + } + sSystemFontMap = systemFonts; + + FileInputStream fallbackIn = new FileInputStream(configFilename); + List<FontFamily> families = new ArrayList<FontFamily>(); + families.add(makeFamilyFromParsed(systemFontConfig.get(0))); + for (Family f : FontListParser.parse(fallbackIn)) { + families.add(makeFamilyFromParsed(f)); + } + FontFamily[] familyArray = families.toArray(new FontFamily[families.size()]); + setDefault(Typeface.createFromFamilies(familyArray)); + } catch (RuntimeException e) { + Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)"); + // TODO: normal in non-Minikin case, remove or make error when Minikin-only + } catch (FileNotFoundException e) { + Log.e(TAG, "Error opening " + configFilename); + } catch (IOException e) { + Log.e(TAG, "Error reading " + configFilename); + } catch (XmlPullParserException e) { + Log.e(TAG, "XML parse exception for " + configFilename); + } + + // Set up defaults and typefaces exposed in public API DEFAULT = create((String) null, 0); DEFAULT_BOLD = create((String) null, Typeface.BOLD); SANS_SERIF = create("sans-serif", 0); SERIF = create("serif", 0); MONOSPACE = create("monospace", 0); - + sDefaults = new Typeface[] { DEFAULT, DEFAULT_BOLD, @@ -215,6 +288,7 @@ public class Typeface { }; } + @Override protected void finalize() throws Throwable { try { nativeUnref(native_instance); @@ -252,4 +326,5 @@ public class Typeface { private static native long nativeCreateFromAsset(AssetManager mgr, String path); private static native long nativeCreateFromFile(String path); private static native long nativeCreateFromArray(long[] familyArray); + private static native void nativeSetDefault(long native_instance); } |