Migrate com.facebook.react.views.text.FontMetricsUtil to Kotlin (#47510)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47510

As per title.

Changelog: [Internal]

Reviewed By: tdn120

Differential Revision: D65598073

fbshipit-source-id: 401750f2df9bd434a8310f107f46a686bfa6601c
This commit is contained in:
Fabrizio Cucci
2024-11-08 06:10:22 -08:00
committed by Facebook GitHub Bot
parent e64513bf4e
commit 697a422bf5
3 changed files with 75 additions and 68 deletions
@@ -7200,9 +7200,9 @@ public final class com/facebook/react/views/text/DefaultStyleValuesUtil {
public static final fun getDefaultTextColorHint (Landroid/content/Context;)Landroid/content/res/ColorStateList;
}
public class com/facebook/react/views/text/FontMetricsUtil {
public fun <init> ()V
public static fun getFontMetrics (Ljava/lang/CharSequence;Landroid/text/Layout;Landroid/text/TextPaint;Landroid/content/Context;)Lcom/facebook/react/bridge/WritableArray;
public final class com/facebook/react/views/text/FontMetricsUtil {
public static final field INSTANCE Lcom/facebook/react/views/text/FontMetricsUtil;
public static final fun getFontMetrics (Ljava/lang/CharSequence;Landroid/text/Layout;Landroid/text/TextPaint;Landroid/content/Context;)Lcom/facebook/react/bridge/WritableArray;
}
public abstract class com/facebook/react/views/text/ReactBaseTextShadowNode : com/facebook/react/uimanager/LayoutShadowNode {
@@ -1,65 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.views.text;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.TextPaint;
import android.util.DisplayMetrics;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableArray;
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++) {
boolean endsWithNewLine = text.length() > 0 && text.charAt(layout.getLineEnd(i) - 1) == '\n';
float lineWidth = endsWithNewLine ? layout.getLineMax(i) : layout.getLineWidth(i);
Rect bounds = new Rect();
layout.getLineBounds(i, bounds);
WritableMap line = Arguments.createMap();
line.putDouble("x", layout.getLineLeft(i) / dm.density);
line.putDouble("y", bounds.top / dm.density);
line.putDouble("width", lineWidth / 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", capHeight);
line.putDouble("xHeight", xHeight);
line.putString(
"text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString());
lines.pushMap(line);
}
return lines;
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.views.text
import android.content.Context
import android.graphics.Rect
import android.text.Layout
import android.text.TextPaint
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableArray
public object FontMetricsUtil {
private const val CAP_HEIGHT_MEASUREMENT_TEXT = "T"
private const val X_HEIGHT_MEASUREMENT_TEXT = "x"
private const val AMPLIFICATION_FACTOR = 100f
@JvmStatic
public fun getFontMetrics(
text: CharSequence,
layout: Layout,
paint: TextPaint,
context: Context
): WritableArray {
val dm = context.resources.displayMetrics
val 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.
val paintCopy = TextPaint(paint).apply { textSize *= AMPLIFICATION_FACTOR }
val capHeightBounds = Rect()
paintCopy.getTextBounds(
CAP_HEIGHT_MEASUREMENT_TEXT, 0, CAP_HEIGHT_MEASUREMENT_TEXT.length, capHeightBounds)
val capHeight = capHeightBounds.height() / AMPLIFICATION_FACTOR / dm.density
val xHeightBounds = Rect()
paintCopy.getTextBounds(
X_HEIGHT_MEASUREMENT_TEXT, 0, X_HEIGHT_MEASUREMENT_TEXT.length, xHeightBounds)
val xHeight = xHeightBounds.height() / AMPLIFICATION_FACTOR / dm.density
for (i in 0 until layout.lineCount) {
val endsWithNewLine = text.isNotEmpty() && text[layout.getLineEnd(i) - 1] == '\n'
val lineWidth = if (endsWithNewLine) layout.getLineMax(i) else layout.getLineWidth(i)
val bounds = Rect()
layout.getLineBounds(i, bounds)
val line =
Arguments.createMap().apply {
putDouble("x", (layout.getLineLeft(i) / dm.density).toDouble())
putDouble("y", (bounds.top / dm.density).toDouble())
putDouble("width", (lineWidth / dm.density).toDouble())
putDouble("height", (bounds.height() / dm.density).toDouble())
putDouble("descender", (layout.getLineDescent(i) / dm.density).toDouble())
putDouble("ascender", (-layout.getLineAscent(i) / dm.density).toDouble())
putDouble("baseline", (layout.getLineBaseline(i) / dm.density).toDouble())
putDouble("capHeight", capHeight.toDouble())
putDouble("xHeight", xHeight.toDouble())
putString(
"text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString())
}
lines.pushMap(line)
}
return lines
}
}