Merge "Introduce setFallbackTypeface" into oc-dev

am: 729a515e5e

Change-Id: I3e88a1d51b8a8be7f932964917218a28ae23c5c9
This commit is contained in:
Seigo Nonaka
2017-04-13 19:38:09 +00:00
committed by android-build-merger
10 changed files with 239 additions and 41 deletions

View File

@@ -13783,6 +13783,7 @@ package android.graphics {
ctor public Typeface.Builder(java.lang.String);
ctor public Typeface.Builder(android.content.res.AssetManager, java.lang.String);
method public android.graphics.Typeface build();
method public android.graphics.Typeface.Builder setFallback(java.lang.String);
method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException;
method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
method public android.graphics.Typeface.Builder setItalic(boolean);

View File

@@ -14550,6 +14550,7 @@ package android.graphics {
ctor public Typeface.Builder(java.lang.String);
ctor public Typeface.Builder(android.content.res.AssetManager, java.lang.String);
method public android.graphics.Typeface build();
method public android.graphics.Typeface.Builder setFallback(java.lang.String);
method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException;
method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
method public android.graphics.Typeface.Builder setItalic(boolean);

View File

@@ -13825,6 +13825,7 @@ package android.graphics {
ctor public Typeface.Builder(java.lang.String);
ctor public Typeface.Builder(android.content.res.AssetManager, java.lang.String);
method public android.graphics.Typeface build();
method public android.graphics.Typeface.Builder setFallback(java.lang.String);
method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException;
method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
method public android.graphics.Typeface.Builder setItalic(boolean);

View File

@@ -592,15 +592,11 @@ public class FontsContract {
int weight, boolean italic, @Nullable String fallbackFontName) {
final Map<Uri, ByteBuffer> uriBuffer =
prepareFontData(context, fonts, cancellationSignal);
Typeface typeface = new Typeface.Builder(fonts, uriBuffer)
return new Typeface.Builder(fonts, uriBuffer)
.setFallback(fallbackFontName)
.setWeight(weight)
.setItalic(italic)
.build();
// TODO: Use Typeface fallback instead.
if (typeface == null) {
typeface = Typeface.create(fallbackFontName, Typeface.NORMAL);
}
return typeface;
}
/**

View File

@@ -45,21 +45,24 @@ namespace android {
constexpr jint RESOLVE_BY_FONT_TABLE = -1;
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
: langId(langId), variant(variant), allowUnsupportedFont(false) {}
uint32_t langId;
int variant;
bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
};
static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
NativeFamilyBuilder* builder = new NativeFamilyBuilder();
NativeFamilyBuilder* builder;
if (lang != nullptr) {
ScopedUtfChars str(env, lang);
builder->langId = minikin::FontStyle::registerLanguageList(str.c_str());
builder = new NativeFamilyBuilder(
minikin::FontStyle::registerLanguageList(str.c_str()), variant);
} else {
builder->langId = minikin::FontStyle::registerLanguageList("");
builder = new NativeFamilyBuilder(minikin::FontStyle::registerLanguageList(""), variant);
}
builder->variant = variant;
return reinterpret_cast<jlong>(builder);
}
@@ -67,12 +70,22 @@ static jlong FontFamily_create(jlong builderPtr) {
if (builderPtr == 0) {
return 0;
}
std::unique_ptr<NativeFamilyBuilder> builder(
reinterpret_cast<NativeFamilyBuilder*>(builderPtr));
std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
builder->langId, builder->variant, std::move(builder->fonts));
if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) {
return 0;
}
return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
}
static void FontFamily_allowUnsupportedFont(jlong builderPtr) {
if (builderPtr == 0) {
return;
}
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
FontFamilyWrapper* family = new FontFamilyWrapper(
std::make_shared<minikin::FontFamily>(
builder->langId, builder->variant, std::move(builder->fonts)));
delete builder;
return reinterpret_cast<jlong>(family);
builder->allowUnsupportedFont = true;
}
static void FontFamily_abort(jlong builderPtr) {
@@ -258,6 +271,7 @@ static void FontFamily_addAxisValue(jlong builderPtr, jint tag, jfloat value) {
static const JNINativeMethod gFontFamilyMethods[] = {
{ "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
{ "nCreateFamily", "(J)J", (void*)FontFamily_create },
{ "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont },
{ "nAbort", "(J)V", (void*)FontFamily_abort },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },

View File

@@ -42,6 +42,13 @@ static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandl
return reinterpret_cast<jlong>(face);
}
static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance,
jint weight, jboolean italic) {
Typeface* baseTypeface = reinterpret_cast<Typeface*>(nativeInstance);
return reinterpret_cast<jlong>(
Typeface::createFromTypefaceWithStyle(baseTypeface, weight, italic));
}
static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle,
jobject listOfAxis) {
std::vector<minikin::FontVariation> variations;
@@ -75,6 +82,11 @@ static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) {
return face->fSkiaStyle;
}
static jint Typeface_getBaseWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
return face->fBaseWeight;
}
static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray) {
ScopedLongArrayRO families(env, familyArray);
std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
@@ -113,11 +125,14 @@ static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle)
static const JNINativeMethod gTypefaceMethods[] = {
{ "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
{ "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J",
(void*)Typeface_createFromTypefaceWithExactStyle },
{ "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
(void*)Typeface_createFromTypefaceWithVariation },
{ "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias },
{ "nativeUnref", "(J)V", (void*)Typeface_unref },
{ "nativeGetStyle", "(J)I", (void*)Typeface_getStyle },
{ "nativeGetBaseWeight", "(J)I", (void*)Typeface_getBaseWeight },
{ "nativeCreateFromArray", "([J)J",
(void*)Typeface_createFromArray },
{ "nativeSetDefault", "(J)V", (void*)Typeface_setDefault },

View File

@@ -52,12 +52,19 @@ public class FontFamily {
mBuilderPtr = nInitBuilder(lang, variant);
}
public void freeze() {
/**
* Finalize the FontFamily creation.
*
* @return boolean returns false if some error happens in native code, e.g. broken font file is
* passed, etc.
*/
public boolean freeze() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen");
}
mNativePtr = nCreateFamily(mBuilderPtr);
mBuilderPtr = 0;
return mNativePtr != 0;
}
public void abortCreation() {
@@ -143,6 +150,25 @@ public class FontFamily {
isItalic);
}
/**
* Allow creating unsupported FontFamily.
*
* For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
* to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
* encoded with Unicode code points, etc. Without calling this method, the freeze() method will
* return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
* freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
* the top of the fallback chain but is never used. if we don't create this empty FontFamily
* and put it at top, bad things (performance regressions, unexpected glyph selection) will
* happen.
*/
public void allowUnsupportedFont() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("Unable to allow unsupported font.");
}
nAllowUnsupportedFont(mBuilderPtr);
}
// TODO: Remove once internal user stop using private API.
private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -153,6 +179,9 @@ public class FontFamily {
@CriticalNative
private static native long nCreateFamily(long mBuilderPtr);
@CriticalNative
private static native void nAllowUnsupportedFont(long builderPtr);
@CriticalNative
private static native void nAbort(long mBuilderPtr);

View File

@@ -108,6 +108,7 @@ public class Typeface {
/**
* Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
*/
@GuardedBy("sLock")
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
static Typeface sDefaultTypeface;
@@ -129,6 +130,7 @@ public class Typeface {
public static final int BOLD_ITALIC = 3;
private int mStyle = 0;
private int mBaseWeight = 0;
// Value for weight and italic. Indicates the value is resolved by font metadata.
// Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -180,7 +182,9 @@ public class Typeface {
if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
fontFamily.freeze();
if (!fontFamily.freeze()) {
return null;
}
FontFamily[] families = {fontFamily};
typeface = createFromFamiliesWithDefault(families);
sDynamicTypefaceCache.put(key, typeface);
@@ -241,6 +245,10 @@ public class Typeface {
return null;
}
}
// Due to backward compatibility, even if the font is not supported by our font stack,
// we need to place the empty font at the first place. The typeface with empty font
// behaves different from default typeface especially in fallback font selection.
fontFamily.allowUnsupportedFont();
fontFamily.freeze();
FontFamily[] familyChain = { fontFamily };
typeface = createFromFamiliesWithDefault(familyChain);
@@ -393,7 +401,11 @@ public class Typeface {
IoUtils.closeQuietly(fd);
}
}
fontFamily.freeze();
if (!fontFamily.freeze()) {
callback.onTypefaceRequestFailed(
FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
return;
}
Typeface typeface = Typeface.createFromFamiliesWithDefault(new FontFamily[] { fontFamily });
synchronized (sDynamicTypefaceCache) {
String key = createProviderUid(request.getProviderAuthority(), request.getQuery());
@@ -526,6 +538,7 @@ public class Typeface {
private FontsContract.FontInfo[] mFonts;
private Map<Uri, ByteBuffer> mFontBuffers;
private String mFallbackFamilyName;
private int mWeight = RESOLVE_BY_FONT_TABLE;
@@ -665,6 +678,33 @@ public class Typeface {
return this;
}
/**
* Sets a fallback family name.
*
* By specifying a fallback family name, a fallback Typeface will be returned if the
* {@link #build} method fails to create a Typeface from the provided font. The fallback
* family will be resolved with the provided weight and italic information specified by
* {@link #setWeight} and {@link #setItalic}.
*
* If {@link #setWeight} is not called, the fallback family keeps the default weight.
* Similary, if {@link #setItalic} is not called, the fallback family keeps the default
* italic information. For example, calling {@code builder.setFallback("sans-serif-light")}
* is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in
* terms of fallback. The default weight and italic information are overridden by calling
* {@link #setWeight} and {@link #setItalic}. For example, if a Typeface is constructed
* using {@code builder.setFallback("sans-serif-light").setWeight(700)}, the fallback text
* will render as sans serif bold.
*
* @param familyName A family name to be used for fallback if the provided font can not be
* used. By passing {@code null}, build() returns {@code null}.
* If {@link #setFallback} is not called on the builder, {@code null}
* is assumed.
*/
public Builder setFallback(@Nullable String familyName) {
mFallbackFamilyName = familyName;
return this;
}
/**
* Creates a unique id for a given AssetManager and asset path.
*
@@ -697,6 +737,54 @@ public class Typeface {
return builder.toString();
}
private static final Object sLock = new Object();
// TODO: Unify with Typeface.sTypefaceCache.
@GuardedBy("sLock")
private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
new LongSparseArray<>(3);
private Typeface resolveFallbackTypeface() {
if (mFallbackFamilyName == null) {
return null;
}
Typeface base = sSystemFontMap.get(mFallbackFamilyName);
if (base == null) {
base = sDefaultTypeface;
}
if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) {
return base;
}
final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mBaseWeight : mWeight;
final boolean italic =
(mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
final int key = weight << 1 | (italic ? 1 : 0);
Typeface typeface;
synchronized(sLock) {
SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance);
if (innerCache != null) {
typeface = innerCache.get(key);
if (typeface != null) {
return typeface;
}
}
typeface = new Typeface(
nativeCreateFromTypefaceWithExactStyle(
base.native_instance, weight, italic));
if (innerCache == null) {
innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
sTypefaceCache.put(base.native_instance, innerCache);
}
innerCache.put(key, typeface);
}
return typeface;
}
/**
* Generates new Typeface from specified configuration.
*
@@ -712,26 +800,30 @@ public class Typeface {
final FontFamily fontFamily = new FontFamily();
if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) {
fontFamily.abortCreation();
return null;
return resolveFallbackTypeface();
}
if (!fontFamily.freeze()) {
return resolveFallbackTypeface();
}
fontFamily.freeze();
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families);
} catch (IOException e) {
return null;
return resolveFallbackTypeface();
}
} else if (mAssetManager != null) { // set source by setSourceFromAsset()
final String key = createAssetUid(mAssetManager, mPath, mTtcIndex, mAxes);
synchronized (sDynamicTypefaceCache) {
synchronized (sLock) {
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
final FontFamily fontFamily = new FontFamily();
if (!fontFamily.addFontFromAssetManager(mAssetManager, mPath, mTtcIndex,
true /* isAsset */, mTtcIndex, mWeight, mItalic, mAxes)) {
fontFamily.abortCreation();
return null;
return resolveFallbackTypeface();
}
if (!fontFamily.freeze()) {
return resolveFallbackTypeface();
}
fontFamily.freeze();
FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families);
sDynamicTypefaceCache.put(key, typeface);
@@ -741,9 +833,11 @@ public class Typeface {
final FontFamily fontFamily = new FontFamily();
if (!fontFamily.addFont(mPath, mTtcIndex, mAxes, mWeight, mItalic)) {
fontFamily.abortCreation();
return null;
return resolveFallbackTypeface();
}
if (!fontFamily.freeze()) {
return resolveFallbackTypeface();
}
fontFamily.freeze();
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families);
} else if (mFonts != null) {
@@ -870,12 +964,34 @@ public class Typeface {
throw new NullPointerException(); // for backward compatibility
}
if (sFallbackFonts != null) {
Typeface typeface = new Builder(mgr, path).build();
if (typeface != null) {
return typeface;
synchronized (sLock) {
Typeface typeface = new Builder(mgr, path).build();
if (typeface != null) return typeface;
final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
null /* axes */);
typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
null /* axes */)) {
// Due to backward compatibility, even if the font is not supported by our font
// stack, we need to place the empty font at the first place. The typeface with
// empty font behaves different from default typeface especially in fallback
// font selection.
fontFamily.allowUnsupportedFont();
fontFamily.freeze();
final FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
} else {
fontFamily.abortCreation();
}
}
}
// For the compatibility reasons, throw runtime exception if failed to create Typeface.
throw new RuntimeException("Font asset not found " + path);
}
@@ -910,17 +1026,20 @@ public class Typeface {
* @return The new typeface.
*/
public static Typeface createFromFile(@Nullable String path) {
if (path == null) {
// For the compatibility reasons, need to throw NPE if the argument is null.
// See android.graphics.cts.TypefaceTest#testCreateFromFileByFileNameNull
throw new NullPointerException();
}
if (sFallbackFonts != null) {
Typeface typeface = new Builder(path).build();
if (typeface != null) {
// For the compatibility reasons, throw runtime exception if failed to create
// Typeface.
return typeface;
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
// Due to backward compatibility, even if the font is not supported by our font
// stack, we need to place the empty font at the first place. The typeface with
// empty font behaves different from default typeface especially in fallback font
// selection.
fontFamily.allowUnsupportedFont();
fontFamily.freeze();
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families);
} else {
fontFamily.abortCreation();
}
}
throw new RuntimeException("Font not found " + path);
@@ -964,6 +1083,7 @@ public class Typeface {
native_instance = ni;
mStyle = nativeGetStyle(ni);
mBaseWeight = nativeGetBaseWeight(ni);
}
private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
@@ -988,7 +1108,11 @@ public class Typeface {
Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
}
}
fontFamily.freeze();
if (!fontFamily.freeze()) {
// Treat as system error since reaching here means that a system pre-installed font
// can't be used by our font stack.
Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
}
return fontFamily;
}
@@ -1129,12 +1253,15 @@ public class Typeface {
}
private static native long nativeCreateFromTypeface(long native_instance, int style);
private static native long nativeCreateFromTypefaceWithExactStyle(
long native_instance, int weight, boolean italic);
// TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
private static native long nativeCreateFromTypefaceWithVariation(
long native_instance, List<FontVariationAxis> axes);
private static native long nativeCreateWeightAlias(long native_instance, int weight);
private static native void nativeUnref(long native_instance);
private static native int nativeGetStyle(long native_instance);
private static native int nativeGetBaseWeight(long native_instance);
private static native long nativeCreateFromArray(long[] familyArray);
private static native void nativeSetDefault(long native_instance);
private static native int[] nativeGetSupportedAxes(long native_instance);

View File

@@ -71,6 +71,18 @@ Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) {
return result;
}
Typeface* Typeface::createFromTypefaceWithStyle(Typeface* base, int weight, bool italic) {
Typeface* resolvedFace = Typeface::resolveDefault(base);
Typeface* result = new Typeface();
if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
result->fBaseWeight = weight;
result->fStyle = minikin::FontStyle(weight / 100, italic);
result->fSkiaStyle = resolvedFace->fSkiaStyle;
}
return result;
}
Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src,
const std::vector<minikin::FontVariation>& variations) {
Typeface* resolvedFace = Typeface::resolveDefault(src);

View File

@@ -42,6 +42,8 @@ struct ANDROID_API Typeface {
static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style);
static Typeface* createFromTypefaceWithStyle(Typeface* base, int weight, bool italic);
static Typeface* createFromTypefaceWithVariation(Typeface* src,
const std::vector<minikin::FontVariation>& variations);