Skip to content

Commit

Permalink
add support for native/downloadable fonts (#23865)
Browse files Browse the repository at this point in the history
Summary:
Android API 26 and Android Support Library 26 added support for font resource type and native/downloadable fonts. It allows apps to easily download fonts from online providers, but also use of various font weights other than normal and bold, like medium. So it deprecated APIs for asset fonts, and should be removed in the future.

Advantages:
- Just copy font files in res/font and use it specifying filename (without extension) in fontFamily
- Define custom font-family using XML file (in res/font) and font files, it may have many weights and styles. See PR for example.
- Define configuration to download fonts from online font providers, and use it.

See https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml and https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts

[Android] [Changed] - add support for custom/downloadable fonts
Pull Request resolved: #23865

Differential Revision: D14506542

Pulled By: hramos

fbshipit-source-id: 67ba3148fb4b548cdbc779213cf6c1b2c3baffd2
  • Loading branch information
dulmandakh authored and facebook-github-bot committed Apr 10, 2019
1 parent d338a60 commit f01c4e2
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 15 deletions.
5 changes: 5 additions & 0 deletions RNTester/android/app/src/main/res/font/srisakdi.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
<font app:fontStyle="normal" app:fontWeight="400" app:font="@font/srisakdi_regular"/>
<font app:fontStyle="normal" app:fontWeight="700" app:font="@font/srisakdi_bold" />
</font-family>
Binary file not shown.
Binary file not shown.
8 changes: 8 additions & 0 deletions RNTester/js/TextExample.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ class TextExample extends React.Component<{}> {
<Text style={{fontFamily: 'notoserif', fontStyle: 'italic'}}>
NotoSerif Italic (Missing Font file)
</Text>
<Text style={{fontFamily: 'srisakdi'}}>Srisakdi Regular</Text>
<Text
style={{
fontFamily: 'srisakdi',
fontWeight: 'bold',
}}>
Srisakdi Bold
</Text>
</View>
</View>
</RNTesterBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

package com.facebook.react.views.text;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Paint;
import android.graphics.Typeface;
Expand All @@ -29,31 +31,30 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
* Fonts are retrieved and cached using the {@link ReactFontManager}
*/

private final AssetManager mAssetManager;

private final int mStyle;
private final int mWeight;
private final @Nullable String mFontFamily;
private final Context mContext;

public CustomStyleSpan(
int fontStyle,
int fontWeight,
@Nullable String fontFamily,
AssetManager assetManager) {
@Nonnull Context context) {
mStyle = fontStyle;
mWeight = fontWeight;
mFontFamily = fontFamily;
mAssetManager = assetManager;
mContext = context;
}

@Override
public void updateDrawState(TextPaint ds) {
apply(ds, mStyle, mWeight, mFontFamily, mAssetManager);
apply(ds, mStyle, mWeight, mFontFamily, mContext);
}

@Override
public void updateMeasureState(TextPaint paint) {
apply(paint, mStyle, mWeight, mFontFamily, mAssetManager);
public void updateMeasureState(@Nonnull TextPaint paint) {
apply(paint, mStyle, mWeight, mFontFamily, mContext);
}

/**
Expand Down Expand Up @@ -82,7 +83,7 @@ private static void apply(
int style,
int weight,
@Nullable String family,
AssetManager assetManager) {
Context context) {
int oldStyle;
Typeface typeface = paint.getTypeface();
if (typeface == null) {
Expand All @@ -103,7 +104,7 @@ private static void apply(
}

if (family != null) {
typeface = ReactFontManager.getInstance().getTypeface(family, want, assetManager);
typeface = ReactFontManager.getInstance().getTypeface(family, want, context);
} else if (typeface != null) {
// TODO(t9055065): Fix custom fonts getting applied to text children with different style
typeface = Typeface.create(typeface, want);
Expand All @@ -116,5 +117,4 @@ private static void apply(
}
paint.setSubpixelText(true);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private static void buildSpannedFromShadowNode(
textShadowNode.mFontStyle,
textShadowNode.mFontWeight,
textShadowNode.mFontFamily,
textShadowNode.getThemedContext().getAssets())));
textShadowNode.getThemedContext())));
}
if (textShadowNode.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.util.SparseArray;

import androidx.core.content.res.ResourcesCompat;


/**
* Class responsible to load and cache Typeface objects. It will first try to load typefaces inside
* the assets/fonts folder and if it doesn't find the right Typeface in that folder will fall back
Expand All @@ -37,9 +41,11 @@ public class ReactFontManager {
private static ReactFontManager sReactFontManagerInstance;

private Map<String, FontFamily> mFontCache;
private Map<String, Typeface> mTypeCache;

private ReactFontManager() {
mFontCache = new HashMap<>();
mTypeCache = new HashMap<>();
}

public static ReactFontManager getInstance() {
Expand All @@ -49,8 +55,7 @@ public static ReactFontManager getInstance() {
return sReactFontManagerInstance;
}

public
@Nullable Typeface getTypeface(
private @Nullable Typeface getTypeface(
String fontFamilyName,
int style,
AssetManager assetManager) {
Expand All @@ -71,6 +76,33 @@ public static ReactFontManager getInstance() {
return typeface;
}

public @Nullable Typeface getTypeface(
String fontFamilyName,
int style,
Context context) {
Typeface font = mTypeCache.get(fontFamilyName);

if (font != null) {
return Typeface.create(
font,
style
);
}

int fontId = context.getResources().getIdentifier(fontFamilyName, "font", context.getPackageName());
if (fontId != 0) {
font = ResourcesCompat.getFont(context, fontId);
if (font != null) {
mTypeCache.put(fontFamilyName, font);
return Typeface.create(
font,
style
);
}
}
return getTypeface(fontFamilyName, style, context.getAssets());
}

/**
* Add additional font family, or replace the exist one in the font memory cache.
* @param style
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ private static void buildSpannableFromFragment(
textAttributes.mFontStyle,
textAttributes.mFontWeight,
textAttributes.mFontFamily,
context.getAssets())));
context)));
}
if (textAttributes.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void setFontFamily(ReactEditText view, String fontFamily) {
Typeface newTypeface = ReactFontManager.getInstance().getTypeface(
fontFamily,
style,
view.getContext().getAssets());
view.getContext());
view.setTypeface(newTypeface);
}

Expand Down

0 comments on commit f01c4e2

Please sign in to comment.