Skip to content

Commit

Permalink
Fix onTextLayout metrics on Android when using alignText
Browse files Browse the repository at this point in the history
Summary:
With this, we send the correct x position when using center or right aligned text. In order to accomplish this though, we have to pass the text alignment into the Layout object that we create.

Also update RNTester to allow us to try different alignments.

Reviewed By: sahrens

Differential Revision: D10316494

fbshipit-source-id: 11c7d2a59e636528f12211168acb46f16b54a126
  • Loading branch information
Mehdi Mulani authored and facebook-github-bot committed Oct 17, 2018
1 parent b951499 commit 1c240ae
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 24 deletions.
53 changes: 49 additions & 4 deletions RNTester/js/Shared/TextLegend.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class TextLegend extends React.Component<*, *> {
state = {
textMetrics: [],
language: 'english',
alignment: 'left',
fontSize: 50,
};

render() {
Expand Down Expand Up @@ -50,6 +52,18 @@ class TextLegend extends React.Component<*, *> {
};
return (
<View>
<Text
onPress={() =>
this.setState(prevState => ({fontSize: prevState.fontSize + 3}))
}>
Increase size
</Text>
<Text
onPress={() =>
this.setState(prevState => ({fontSize: prevState.fontSize - 3}))
}>
Decrease size
</Text>
<Picker
selectedValue={this.state.language}
onValueChange={itemValue => this.setState({language: itemValue})}>
Expand Down Expand Up @@ -179,17 +193,48 @@ class TextLegend extends React.Component<*, *> {
}}>
End of text
</Text>,
<View
key="start of text view"
style={{
top: y,
height: height,
width: 1,
left: x,
position: 'absolute',
backgroundColor: 'brown',
}}
/>,
<Text
key="start of text text"
style={{
top: y,
left: x + 5,
position: 'absolute',
color: 'brown',
}}>
Start of text
</Text>,
];
},
)}
<Text
onTextLayout={event =>
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]}
</Text>
</View>
<Picker
selectedValue={this.state.alignment}
onValueChange={itemValue => this.setState({alignment: itemValue})}>
<Picker.Item label="Left align" value="left" />
<Picker.Item label="Center align" value="center" />
<Picker.Item label="Right align" value="right" />
</Picker>
</View>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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))) {
Expand All @@ -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)
Expand All @@ -108,7 +122,7 @@ public long measure(
text,
textPaint,
boring.width,
Layout.Alignment.ALIGN_NORMAL,
alignment,
1.f,
0.f,
boring,
Expand All @@ -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)
Expand Down

0 comments on commit 1c240ae

Please sign in to comment.