public static MeasureResult layout()

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);
  }