diff --git a/RNTester/js/Shared/TextLegend.js b/RNTester/js/Shared/TextLegend.js
index 3708ddeaf0c14a..56cf17607c6b64 100644
--- a/RNTester/js/Shared/TextLegend.js
+++ b/RNTester/js/Shared/TextLegend.js
@@ -17,6 +17,8 @@ class TextLegend extends React.Component<*, *> {
state = {
textMetrics: [],
language: 'english',
+ alignment: 'left',
+ fontSize: 50,
};
render() {
@@ -50,6 +52,18 @@ class TextLegend extends React.Component<*, *> {
};
return (
+
+ this.setState(prevState => ({fontSize: prevState.fontSize + 3}))
+ }>
+ Increase size
+
+
+ this.setState(prevState => ({fontSize: prevState.fontSize - 3}))
+ }>
+ Decrease size
+
this.setState({language: itemValue})}>
@@ -179,17 +193,48 @@ class TextLegend extends React.Component<*, *> {
}}>
End of text
,
+ ,
+
+ Start of text
+ ,
];
},
)}
- this.setState({textMetrics: event.nativeEvent.lines})
- }
- style={{fontSize: 50}}>
+ onTextLayout={event => {
+ this.setState({textMetrics: event.nativeEvent.lines});
+ }}
+ style={{
+ fontSize: this.state.fontSize,
+ textAlign: this.state.alignment,
+ }}>
{PANGRAMS[this.state.language]}
+ this.setState({alignment: itemValue})}>
+
+
+
+
);
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java
index 2a235a355d4e21..419985323c3f2c 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java
@@ -17,30 +17,38 @@
import com.facebook.react.bridge.WritableMap;
public class FontMetricsUtil {
+
+ private static final String CAP_HEIGHT_MEASUREMENT_TEXT = "T";
+ private static final String X_HEIGHT_MEASUREMENT_TEXT = "x";
+ private static final float AMPLIFICATION_FACTOR = 100;
+
public static WritableArray getFontMetrics(CharSequence text, Layout layout, TextPaint paint, Context context) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
WritableArray lines = Arguments.createArray();
+ // To calculate xHeight and capHeight we have to render an "x" and "T" and manually measure their height.
+ // In order to get more precision than Android offers, we blow up the text size by 100 and measure it.
+ // Luckily, text size affects rendering linearly, so we can do this trick.
+ TextPaint paintCopy = new TextPaint(paint);
+ paintCopy.setTextSize(paintCopy.getTextSize() * AMPLIFICATION_FACTOR);
+ Rect capHeightBounds = new Rect();
+ paintCopy.getTextBounds(CAP_HEIGHT_MEASUREMENT_TEXT, 0, CAP_HEIGHT_MEASUREMENT_TEXT.length(), capHeightBounds);
+ double capHeight = capHeightBounds.height() / AMPLIFICATION_FACTOR / dm.density;
+ Rect xHeightBounds = new Rect();
+ paintCopy.getTextBounds(X_HEIGHT_MEASUREMENT_TEXT, 0, X_HEIGHT_MEASUREMENT_TEXT.length(), xHeightBounds);
+ double xHeight = xHeightBounds.height() / AMPLIFICATION_FACTOR / dm.density;
for (int i = 0; i < layout.getLineCount(); i++) {
Rect bounds = new Rect();
layout.getLineBounds(i, bounds);
-
WritableMap line = Arguments.createMap();
- TextPaint paintCopy = new TextPaint(paint);
- paintCopy.setTextSize(paintCopy.getTextSize() * 100);
- Rect capHeightBounds = new Rect();
- paintCopy.getTextBounds("T", 0, 1, capHeightBounds);
- Rect xHeightBounds = new Rect();
- paintCopy.getTextBounds("x", 0, 1, xHeightBounds);
- line.putDouble("x", bounds.left / dm.density);
+ line.putDouble("x", layout.getLineLeft(i) / dm.density);
line.putDouble("y", bounds.top / dm.density);
line.putDouble("width", layout.getLineWidth(i) / dm.density);
line.putDouble("height", bounds.height() / dm.density);
line.putDouble("descender", layout.getLineDescent(i) / dm.density);
line.putDouble("ascender", -layout.getLineAscent(i) / dm.density);
line.putDouble("baseline", layout.getLineBaseline(i) / dm.density);
- line.putDouble(
- "capHeight", capHeightBounds.height() / 100 * paint.getTextSize() / dm.density);
- line.putDouble("xHeight", xHeightBounds.height() / 100 * paint.getTextSize() / dm.density);
+ line.putDouble("capHeight", capHeight);
+ line.putDouble("xHeight", xHeight);
line.putString(
"text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString());
lines.pushMap(line);
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java
index 6e6d2cfef8441b..b1c68b987e9cdb 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java
@@ -181,6 +181,11 @@ private static void buildSpannedFromShadowNode(
}
}
+ protected int getDefaultFontSize() {
+ return mAllowFontScaling ? (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP))
+ : (int) Math.ceil(PixelUtil.toPixelFromDIP(ViewDefaults.FONT_SIZE_SP));
+ }
+
protected static Spannable spannedFromShadowNode(
ReactBaseTextShadowNode textShadowNode, String text) {
SpannableStringBuilder sb = new SpannableStringBuilder();
@@ -199,10 +204,7 @@ protected static Spannable spannedFromShadowNode(
}
if (textShadowNode.mFontSize == UNSET) {
- int defaultFontSize =
- textShadowNode.mAllowFontScaling
- ? (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP))
- : (int) Math.ceil(PixelUtil.toPixelFromDIP(ViewDefaults.FONT_SIZE_SP));
+ int defaultFontSize = textShadowNode.getDefaultFontSize();
ops.add(new SetSpanOperation(0, sb.length(), new AbsoluteSizeSpan(defaultFontSize)));
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java
index 14855f96820530..8b1ba78d131830 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java
@@ -64,6 +64,7 @@ public long measure(
YogaMeasureMode heightMode) {
// TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic)
TextPaint textPaint = sTextPaintInstance;
+ textPaint.setTextSize(mFontSize != UNSET ? mFontSize : getDefaultFontSize());
Layout layout;
Spanned text = Assertions.assertNotNull(
mPreparedSpannableText,
@@ -75,6 +76,19 @@ public long measure(
// technically, width should never be negative, but there is currently a bug in
boolean unconstrainedWidth = widthMode == YogaMeasureMode.UNDEFINED || width < 0;
+ Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL;
+ switch (getTextAlign()) {
+ case Gravity.LEFT:
+ alignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ case Gravity.RIGHT:
+ alignment = Layout.Alignment.ALIGN_OPPOSITE;
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ alignment = Layout.Alignment.ALIGN_CENTER;
+ break;
+ }
+
if (boring == null &&
(unconstrainedWidth ||
(!YogaConstants.isUndefined(desiredWidth) && desiredWidth <= width))) {
@@ -87,13 +101,13 @@ public long measure(
text,
textPaint,
hintWidth,
- Layout.Alignment.ALIGN_NORMAL,
+ alignment,
1.f,
0.f,
mIncludeFontPadding);
} else {
layout = StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, hintWidth)
- .setAlignment(Layout.Alignment.ALIGN_NORMAL)
+ .setAlignment(alignment)
.setLineSpacing(0.f, 1.f)
.setIncludePad(mIncludeFontPadding)
.setBreakStrategy(mTextBreakStrategy)
@@ -108,7 +122,7 @@ public long measure(
text,
textPaint,
boring.width,
- Layout.Alignment.ALIGN_NORMAL,
+ alignment,
1.f,
0.f,
boring,
@@ -121,13 +135,13 @@ public long measure(
text,
textPaint,
(int) width,
- Layout.Alignment.ALIGN_NORMAL,
+ alignment,
1.f,
0.f,
mIncludeFontPadding);
} else {
layout = StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, (int) width)
- .setAlignment(Layout.Alignment.ALIGN_NORMAL)
+ .setAlignment(alignment)
.setLineSpacing(0.f, 1.f)
.setIncludePad(mIncludeFontPadding)
.setBreakStrategy(mTextBreakStrategy)