From 711b78707095e489b316cec96c7ed15fe35aff6f Mon Sep 17 00:00:00 2001 From: Fabrizio Cucci Date: Fri, 8 Nov 2024 04:40:54 -0800 Subject: [PATCH] Migrate com.facebook.react.views.text.FontMetricsUtil to Kotlin Summary: As per title. Changelog: [Internal] Reviewed By: tdn120 Differential Revision: D65598073 --- .../ReactAndroid/api/ReactAndroid.api | 6 +- .../react/views/text/FontMetricsUtil.java | 65 ----------------- .../react/views/text/FontMetricsUtil.kt | 72 +++++++++++++++++++ 3 files changed, 75 insertions(+), 68 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 3a8e8ca785b54d..1585de5f1e42c9 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -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 ()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 { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java deleted file mode 100644 index 84dc328edab3af..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java +++ /dev/null @@ -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; - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.kt new file mode 100644 index 00000000000000..f9446d08a93a20 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.kt @@ -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 + } +}