/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.text;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;
public class CustomStyleSpan extends MetricAffectingSpan {
// Typeface caching is a bit weird: once a Typeface is created, it cannot be changed, so we need
// to cache each font family and each style that they have. Typeface does cache this already in
// Typeface.create(Typeface, style) post API 16, but for that you already need a Typeface.
// Therefore, here we cache one style for each font family, and let Typeface cache all styles for
// that font family. Of course this is not ideal, and especially after adding Typeface loading
// from assets, we will need to have our own caching mechanism for all Typeface creation types.
// TODO: t6866343 add better Typeface caching
private static final Map<String, Typeface> sTypefaceCache = new HashMap<String, Typeface>();
private final int mStyle;
private final int mWeight;
private final @Nullable String mFontFamily;
public CustomStyleSpan(int fontStyle, int fontWeight, @Nullable String fontFamily) {
mStyle = fontStyle;
mWeight = fontWeight;
mFontFamily = fontFamily;
}
@Override
public void updateDrawState(TextPaint ds) {
apply(ds, mStyle, mWeight, mFontFamily);
}
@Override
public void updateMeasureState(TextPaint paint) {
apply(paint, mStyle, mWeight, mFontFamily);
}
/**
* Returns {@link Typeface#NORMAL} or {@link Typeface#ITALIC}.
*/
public int getStyle() {
return (mStyle == ReactTextShadowNode.UNSET ? 0 : mStyle);
}
/**
* Returns {@link Typeface#NORMAL} or {@link Typeface#BOLD}.
*/
public int getWeight() {
return (mWeight == ReactTextShadowNode.UNSET ? 0 : mWeight);
}
/**
* Returns the font family set for this StyleSpan.
*/
public @Nullable String getFontFamily() {
return mFontFamily;
}
private static void apply(Paint paint, int style, int weight, @Nullable String family) {
int oldStyle;
Typeface typeface = paint.getTypeface();
if (typeface == null) {
oldStyle = 0;
} else {
oldStyle = typeface.getStyle();
}
int want = 0;
if ((weight == Typeface.BOLD) ||
((oldStyle & Typeface.BOLD) != 0 && weight == ReactTextShadowNode.UNSET)) {
want |= Typeface.BOLD;
}
if ((style == Typeface.ITALIC) ||
((oldStyle & Typeface.ITALIC) != 0 && style == ReactTextShadowNode.UNSET)) {
want |= Typeface.ITALIC;
}
if (family != null) {
typeface = getOrCreateTypeface(family, want);
}
if (typeface != null) {
paint.setTypeface(Typeface.create(typeface, want));
} else {
paint.setTypeface(Typeface.defaultFromStyle(want));
}
}
private static Typeface getOrCreateTypeface(String family, int style) {
if (sTypefaceCache.get(family) != null) {
return sTypefaceCache.get(family);
}
Typeface typeface = Typeface.create(family, style);
sTypefaceCache.put(family, typeface);
return typeface;
}
}