in litho-rendercore-text/src/main/java/com/facebook/rendercore/text/TextMeasurementUtils.java [76:201]
public static MeasureResult layout(
RenderState.LayoutContext context,
int widthSpec,
int heightSpec,
CharSequence text,
TextRenderUnit renderUnit,
TextStyle textStyle) {
final DebugMeasureListener debugListener = sDebugMeasureListener;
final Context androidContext = context.getAndroidContext();
if (debugListener != null) {
debugListener.onTextMeasured(renderUnit, text, widthSpec, heightSpec);
}
final TextLayoutContext textLayoutContext = new TextLayoutContext();
textLayoutContext.textStyle = textStyle;
if (TextUtils.isEmpty(text) && !textStyle.shouldLayoutEmptyText) {
textLayoutContext.processedText = text;
return new MeasureResult(renderUnit, widthSpec, heightSpec, 0, 0, textLayoutContext);
}
Layout layout =
TextMeasurementUtils.createTextLayout(
androidContext, textStyle, widthSpec, heightSpec, text);
final int layoutWidth =
View.resolveSize(
layout.getWidth()
+ Math.round(textStyle.extraSpacingLeft + textStyle.extraSpacingRight),
widthSpec);
// Adjust height according to the minimum number of lines.
int preferredHeight = LayoutMeasureUtil.getHeight(layout);
int extraSpacingHeight = 0;
if (textStyle.spacingMultiplier > 1f && textStyle.shouldAddExtraSpacingToFistLine) {
final TextPaint paint = layout.getPaint();
final int layoutLineHeight = paint.getFontMetricsInt(null);
extraSpacingHeight = (int) (layoutLineHeight * (textStyle.spacingMultiplier - 1.0f));
preferredHeight += extraSpacingHeight;
}
final int lineCount = layout.getLineCount();
if (lineCount < textStyle.minLines) {
final TextPaint paint = layout.getPaint();
final int layoutLineHeight =
Math.round(
paint.getFontMetricsInt(null) * textStyle.spacingMultiplier + textStyle.extraSpacing);
preferredHeight += layoutLineHeight * (textStyle.minLines - lineCount);
}
final float textHeight = LayoutMeasureUtil.getHeight(layout);
final int capHeightOffset;
if (hasManualSpacing(textStyle)) {
final int[] capHeights = getCapHeightBaselineSpacing(layout.getPaint(), text);
final int baselineOffset = capHeights[BASELINE_OFFSET_INDEX];
capHeightOffset = capHeights[CAP_HEIGHT_OFFSET_INDEX] - textStyle.manualCapSpacing;
preferredHeight -= (capHeightOffset + baselineOffset);
preferredHeight += textStyle.manualBaselineSpacing;
} else {
capHeightOffset = 0;
}
final int layoutHeight = View.resolveSize(preferredHeight, heightSpec);
float textLayoutTranslationY;
switch (textStyle.verticalGravity) {
case CENTER:
textLayoutTranslationY =
(layoutHeight - textHeight) / 2 + extraSpacingHeight - capHeightOffset;
break;
case BOTTOM:
textLayoutTranslationY = layoutHeight - textHeight + extraSpacingHeight - capHeightOffset;
break;
default:
textLayoutTranslationY = extraSpacingHeight - capHeightOffset;
break;
}
// Handle custom text truncation:
CharSequence processedText = text;
if (textStyle.customEllipsisText != null && !textStyle.customEllipsisText.equals("")) {
final int ellipsizedLineNumber = getEllipsizedLineNumber(layout);
if (ellipsizedLineNumber != -1) {
final CharSequence truncated =
truncateText(
text,
textStyle.customEllipsisText,
layout,
ellipsizedLineNumber,
layoutWidth - textStyle.extraSpacingLeft - textStyle.extraSpacingRight);
Layout newLayout =
TextMeasurementUtils.createTextLayout(
androidContext,
textStyle,
View.MeasureSpec.makeMeasureSpec(layoutWidth, View.MeasureSpec.EXACTLY),
heightSpec,
truncated);
processedText = truncated;
layout = newLayout;
}
}
textLayoutContext.processedText = processedText;
textLayoutContext.layout = layout;
if (textStyle.alignment == TextAlignment.TEXT_START) {
textLayoutContext.textLayoutTranslationX = textStyle.extraSpacingLeft;
} else if (textStyle.alignment == TextAlignment.TEXT_END) {
textLayoutContext.textLayoutTranslationX = -textStyle.extraSpacingRight;
}
textLayoutContext.textLayoutTranslationY = textLayoutTranslationY;
if (processedText instanceof Spanned) {
Spanned spanned = (Spanned) processedText;
textLayoutContext.clickableSpans =
spanned.getSpans(0, processedText.length(), ClickableSpan.class);
textLayoutContext.imageSpans = spanned.getSpans(0, processedText.length(), ImageSpan.class);
}
return new MeasureResult(
renderUnit, widthSpec, heightSpec, layoutWidth, layoutHeight, textLayoutContext);
}