Skip to content

Commit c2c4b43

Browse files
mcuelenaerefacebook-github-bot
authored andcommitted
Add Android support for fontVariant prop (#27006)
Summary: Android was missing support for the `fontVariant` prop in TextViews, this PR adds that. ## Changelog [Android] [Added] - Add Android support for fontVariant prop Pull Request resolved: #27006 Test Plan: Since I can't get RNTester to work locally (it crashes when loading `libyoga.so` on `No implementation found for long com.facebook.yoga.YogaNative.jni_YGConfigNew()`), I'll post some screenshots below of our app showing the difference. We are using a slightly different [version](getdelta@10cafca) of this commit, since we're still on 0.60, but the gist remains the same when rebased on master. Before: ![Screenshot_20191025-130325__01](https://user-images.githubusercontent.com/1682432/67566586-7b3f2880-f728-11e9-85c0-57667d645153.jpg) After: ![Screenshot_20191025-130444__01](https://user-images.githubusercontent.com/1682432/67566599-842ffa00-f728-11e9-988a-1b12ee393b83.jpg) Differential Revision: D18179642 Pulled By: mdvacca fbshipit-source-id: 03a050aa76e7bafa0343354dfa778cf74af5abd2
1 parent 6ebd3b0 commit c2c4b43

File tree

8 files changed

+116
-6
lines changed

8 files changed

+116
-6
lines changed

Libraries/DeprecatedPropTypes/DeprecatedTextStylePropTypes.js

-3
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ const DeprecatedTextStylePropTypes = {
5454
| '800'
5555
| '900',
5656
>),
57-
/**
58-
* @platform ios
59-
*/
6057
fontVariant: (ReactPropTypes.arrayOf(
6158
ReactPropTypes.oneOf([
6259
'small-caps',

RNTester/js/examples/Text/TextExample.android.js

+27
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,33 @@ class TextExample extends React.Component<{}> {
574574
This very long text should be clipped and this will not be visible.
575575
</Text>
576576
</RNTesterBlock>
577+
<RNTesterBlock title="Font variants">
578+
<Text style={{fontVariant: ['small-caps']}}>Small Caps{'\n'}</Text>
579+
<Text
580+
style={{
581+
fontFamily: 'Roboto',
582+
fontVariant: ['oldstyle-nums'],
583+
}}>
584+
Old Style nums 0123456789{'\n'}
585+
</Text>
586+
<Text
587+
style={{
588+
fontFamily: 'Roboto',
589+
fontVariant: ['lining-nums'],
590+
}}>
591+
Lining nums 0123456789{'\n'}
592+
</Text>
593+
<Text style={{fontVariant: ['tabular-nums']}}>
594+
Tabular nums{'\n'}
595+
1111{'\n'}
596+
2222{'\n'}
597+
</Text>
598+
<Text style={{fontVariant: ['proportional-nums']}}>
599+
Proportional nums{'\n'}
600+
1111{'\n'}
601+
2222{'\n'}
602+
</Text>
603+
</RNTesterBlock>
577604
<RNTesterBlock title="Include Font Padding">
578605
<View
579606
style={{

ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public class ViewProps {
8383
public static final String FONT_SIZE = "fontSize";
8484
public static final String FONT_WEIGHT = "fontWeight";
8585
public static final String FONT_STYLE = "fontStyle";
86+
public static final String FONT_VARIANT = "fontVariant";
8687
public static final String FONT_FAMILY = "fontFamily";
8788
public static final String LINE_HEIGHT = "lineHeight";
8889
public static final String LETTER_SPACING = "letterSpacing";

ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import android.content.res.AssetManager;
1111
import android.graphics.Paint;
1212
import android.graphics.Typeface;
13+
import android.os.Build;
1314
import android.text.TextPaint;
1415
import android.text.style.MetricAffectingSpan;
1516
import androidx.annotation.NonNull;
@@ -32,27 +33,30 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
3233

3334
private final int mStyle;
3435
private final int mWeight;
36+
private final @Nullable String mFeatureSettings;
3537
private final @Nullable String mFontFamily;
3638

3739
public CustomStyleSpan(
3840
int fontStyle,
3941
int fontWeight,
42+
@Nullable String fontFeatureSettings,
4043
@Nullable String fontFamily,
4144
@NonNull AssetManager assetManager) {
4245
mStyle = fontStyle;
4346
mWeight = fontWeight;
47+
mFeatureSettings = fontFeatureSettings;
4448
mFontFamily = fontFamily;
4549
mAssetManager = assetManager;
4650
}
4751

4852
@Override
4953
public void updateDrawState(TextPaint ds) {
50-
apply(ds, mStyle, mWeight, mFontFamily, mAssetManager);
54+
apply(ds, mStyle, mWeight, mFeatureSettings, mFontFamily, mAssetManager);
5155
}
5256

5357
@Override
5458
public void updateMeasureState(@NonNull TextPaint paint) {
55-
apply(paint, mStyle, mWeight, mFontFamily, mAssetManager);
59+
apply(paint, mStyle, mWeight, mFeatureSettings, mFontFamily, mAssetManager);
5660
}
5761

5862
/** Returns {@link Typeface#NORMAL} or {@link Typeface#ITALIC}. */
@@ -71,9 +75,17 @@ public int getWeight() {
7175
}
7276

7377
private static void apply(
74-
Paint paint, int style, int weight, @Nullable String family, AssetManager assetManager) {
78+
Paint paint,
79+
int style,
80+
int weight,
81+
@Nullable String fontFeatureSettings,
82+
@Nullable String family,
83+
AssetManager assetManager) {
7584
Typeface typeface = ReactTypefaceUtils.applyStyles(
7685
paint.getTypeface(), style, weight, family, assetManager);
86+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
87+
paint.setFontFeatureSettings(fontFeatureSettings);
88+
}
7789
paint.setTypeface(typeface);
7890
paint.setSubpixelText(true);
7991
}

ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java

+18
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import androidx.annotation.Nullable;
1919
import com.facebook.infer.annotation.Assertions;
2020
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
21+
import com.facebook.react.bridge.ReadableArray;
2122
import com.facebook.react.bridge.ReadableMap;
2223
import com.facebook.react.uimanager.IllegalViewOperationException;
2324
import com.facebook.react.uimanager.LayoutShadowNode;
@@ -33,6 +34,7 @@
3334
import java.util.HashMap;
3435
import java.util.List;
3536
import java.util.Map;
37+
import java.util.Objects;
3638

3739
/**
3840
* {@link ReactShadowNode} abstract class for spannable text nodes.
@@ -195,6 +197,7 @@ private static void buildSpannedFromShadowNode(
195197
new CustomStyleSpan(
196198
textShadowNode.mFontStyle,
197199
textShadowNode.mFontWeight,
200+
textShadowNode.mFontFeatureSettings,
198201
textShadowNode.mFontFamily,
199202
textShadowNode.getThemedContext().getAssets())));
200203
}
@@ -357,6 +360,11 @@ protected Spannable spannedFromShadowNode(
357360
*/
358361
protected @Nullable String mFontFamily = null;
359362

363+
/**
364+
* @see android.graphics.Paint#setFontFeatureSettings
365+
*/
366+
protected @Nullable String mFontFeatureSettings = null;
367+
360368
protected boolean mContainsImages = false;
361369
protected Map<Integer, ReactShadowNode> mInlineViews;
362370

@@ -483,6 +491,16 @@ public void setFontWeight(@Nullable String fontWeightString) {
483491
}
484492
}
485493

494+
@ReactProp(name = ViewProps.FONT_VARIANT)
495+
public void setFontVariant(@Nullable ReadableArray fontVariantArray) {
496+
String fontFeatureSettings = ReactTypefaceUtils.parseFontVariant(fontVariantArray);
497+
498+
if (!Objects.equals(fontFeatureSettings, mFontFeatureSettings)) {
499+
mFontFeatureSettings = fontFeatureSettings;
500+
markUpdated();
501+
}
502+
}
503+
486504
@ReactProp(name = ViewProps.FONT_STYLE)
487505
public void setFontStyle(@Nullable String fontStyleString) {
488506
int fontStyle = ReactTypefaceUtils.parseFontStyle(fontStyleString);

ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java

+35
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414
import androidx.annotation.Nullable;
1515

16+
import com.facebook.react.bridge.ReadableArray;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
1621
public class ReactTypefaceUtils {
1722
public static final int UNSET = -1;
1823

@@ -38,6 +43,36 @@ public static int parseFontStyle(@Nullable String fontStyleString) {
3843
return fontStyle;
3944
}
4045

46+
public static @Nullable String parseFontVariant(@Nullable ReadableArray fontVariantArray) {
47+
if (fontVariantArray == null || fontVariantArray.size() == 0) {
48+
return null;
49+
}
50+
51+
List<String> features = new ArrayList<>();
52+
for (int i = 0; i < fontVariantArray.size(); i++) {
53+
// see https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist
54+
switch (fontVariantArray.getString(i)) {
55+
case "small-caps":
56+
features.add("'smcp'");
57+
break;
58+
case "oldstyle-nums":
59+
features.add("'onum'");
60+
break;
61+
case "lining-nums":
62+
features.add("'lnum'");
63+
break;
64+
case "tabular-nums":
65+
features.add("'tnum'");
66+
break;
67+
case "proportional-nums":
68+
features.add("'pnum'");
69+
break;
70+
}
71+
}
72+
73+
return String.join(", ", features);
74+
}
75+
4176
public static Typeface applyStyles(@Nullable Typeface typeface,
4277
int style, int weight, @Nullable String family, AssetManager assetManager) {
4378
int oldStyle;

ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java

+19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import android.view.Gravity;
1414
import androidx.annotation.Nullable;
1515
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
16+
import com.facebook.react.bridge.ReadableArray;
1617
import com.facebook.react.bridge.ReadableMap;
1718
import com.facebook.react.uimanager.PixelUtil;
1819
import com.facebook.react.uimanager.ReactStylesDiffMap;
@@ -92,6 +93,11 @@ public class TextAttributeProps {
9293
*/
9394
protected @Nullable String mFontFamily = null;
9495

96+
/**
97+
* @see android.graphics.Paint#setFontFeatureSettings
98+
*/
99+
protected @Nullable String mFontFeatureSettings = null;
100+
95101
protected boolean mContainsImages = false;
96102
protected float mHeightOfTallestInlineImage = Float.NaN;
97103

@@ -114,6 +120,7 @@ public TextAttributeProps(ReactStylesDiffMap props) {
114120
setFontFamily(getStringProp(ViewProps.FONT_FAMILY));
115121
setFontWeight(getStringProp(ViewProps.FONT_WEIGHT));
116122
setFontStyle(getStringProp(ViewProps.FONT_STYLE));
123+
setFontVariant(getArrayProp(ViewProps.FONT_VARIANT));
117124
setIncludeFontPadding(getBooleanProp(ViewProps.INCLUDE_FONT_PADDING, true));
118125
setTextDecorationLine(getStringProp(ViewProps.TEXT_DECORATION_LINE));
119126
setTextBreakStrategy(getStringProp(ViewProps.TEXT_BREAK_STRATEGY));
@@ -155,6 +162,14 @@ private float getFloatProp(String name, float defaultvalue) {
155162
}
156163
}
157164

165+
private @Nullable ReadableArray getArrayProp(String name) {
166+
if (mProps.hasKey(name)) {
167+
return mProps.getArray(name);
168+
} else {
169+
return null;
170+
}
171+
}
172+
158173
// Returns a line height which takes into account the requested line height
159174
// and the height of the inline images.
160175
public float getEffectiveLineHeight() {
@@ -280,6 +295,10 @@ public void setFontFamily(@Nullable String fontFamily) {
280295
mFontFamily = fontFamily;
281296
}
282297

298+
public void setFontVariant(@Nullable ReadableArray fontVariant) {
299+
mFontFeatureSettings = ReactTypefaceUtils.parseFontVariant(fontVariant);
300+
}
301+
283302
/**
284303
* /* This code is duplicated in ReactTextInputManager /* TODO: Factor into a common place they
285304
* can both use

ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ private static void buildSpannableFromFragment(
9292
new CustomStyleSpan(
9393
textAttributes.mFontStyle,
9494
textAttributes.mFontWeight,
95+
textAttributes.mFontFeatureSettings,
9596
textAttributes.mFontFamily,
9697
context.getAssets())));
9798
}

0 commit comments

Comments
 (0)