/* * 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 com.facebook.common.logging.FLog import com.facebook.react.common.ReactConstants import com.facebook.react.uimanager.PixelUtil import com.facebook.react.uimanager.ViewDefaults /* * Currently, TextAttributes consists of a subset of text props that need to be passed from parent * to child so inheritance can be implemented correctly. An example complexity that causes a prop * to end up in TextAttributes is when multiple props need to be considered together to determine * the rendered aka effective value. For example, to figure out the rendered/effective font size, * you need to take into account the fontSize, maxFontSizeMultiplier, and allowFontScaling props. */ public class TextAttributes { public var allowFontScaling: Boolean = true public var fontSize: Float = Float.NaN public var lineHeight: Float = Float.NaN public var letterSpacing: Float = Float.NaN public var heightOfTallestInlineViewOrImage: Float = Float.NaN @JvmField internal var textTransform: TextTransform = TextTransform.UNSET public fun applyChild(child: TextAttributes): TextAttributes { val result = TextAttributes() // allowFontScaling is always determined by the root Text // component so don't allow the child to overwrite it. result.allowFontScaling = allowFontScaling result.fontSize = if (!child.fontSize.isNaN()) child.fontSize else fontSize result.lineHeight = if (!child.lineHeight.isNaN()) child.lineHeight else lineHeight result.letterSpacing = if (!child.letterSpacing.isNaN()) child.letterSpacing else letterSpacing result.maxFontSizeMultiplier = if (!child.maxFontSizeMultiplier.isNaN()) child.maxFontSizeMultiplier else maxFontSizeMultiplier result.heightOfTallestInlineViewOrImage = if (!child.heightOfTallestInlineViewOrImage.isNaN()) child.heightOfTallestInlineViewOrImage else heightOfTallestInlineViewOrImage result.textTransform = if (child.textTransform != TextTransform.UNSET) child.textTransform else textTransform return result } public var maxFontSizeMultiplier: Float = Float.NaN set(maxFontSizeMultiplier) { if ( maxFontSizeMultiplier != 0f && maxFontSizeMultiplier < 1 && !maxFontSizeMultiplier.isNaN() ) { FLog.w(ReactConstants.TAG, "maxFontSizeMultiplier must be NaN, 0, or >= 1") field = Float.NaN return } field = maxFontSizeMultiplier } public val effectiveFontSize: Int get() { val fontSize = if (!fontSize.isNaN()) fontSize else ViewDefaults.FONT_SIZE_SP return if (allowFontScaling) { Math.ceil(PixelUtil.toPixelFromSP(fontSize, effectiveMaxFontSizeMultiplier).toDouble()) .toInt() } else { Math.ceil(PixelUtil.toPixelFromDIP(fontSize).toDouble()).toInt() } } public val effectiveLineHeight: Float get() { if (lineHeight.isNaN()) { return Float.NaN } val lineHeight: Float = if (allowFontScaling) PixelUtil.toPixelFromSP(lineHeight, effectiveMaxFontSizeMultiplier) else PixelUtil.toPixelFromDIP(lineHeight) // Take into account the requested line height // and the height of the inline images. val useInlineViewHeight = !heightOfTallestInlineViewOrImage.isNaN() && heightOfTallestInlineViewOrImage > lineHeight return if (useInlineViewHeight) heightOfTallestInlineViewOrImage else lineHeight } public val effectiveLetterSpacing: Float get() { if (letterSpacing.isNaN()) { return Float.NaN } val letterSpacingPixels: Float = if (allowFontScaling) PixelUtil.toPixelFromSP(letterSpacing, effectiveMaxFontSizeMultiplier) else PixelUtil.toPixelFromDIP(letterSpacing) // `letterSpacingPixels` and `getEffectiveFontSize` are both in pixels, // yielding an accurate em value. return letterSpacingPixels / effectiveFontSize } public val effectiveMaxFontSizeMultiplier: Float // Never returns NaN get() = if (!maxFontSizeMultiplier.isNaN()) maxFontSizeMultiplier else DEFAULT_MAX_FONT_SIZE_MULTIPLIER override fun toString(): String = """ TextAttributes { getAllowFontScaling(): $allowFontScaling getFontSize(): $fontSize getEffectiveFontSize(): $effectiveFontSize getHeightOfTallestInlineViewOrImage(): $heightOfTallestInlineViewOrImage getLetterSpacing(): $letterSpacing getEffectiveLetterSpacing(): $effectiveLetterSpacing getLineHeight(): $lineHeight getEffectiveLineHeight(): $effectiveLineHeight getTextTransform(): $textTransform getMaxFontSizeMultiplier(): $maxFontSizeMultiplier getEffectiveMaxFontSizeMultiplier(): $effectiveMaxFontSizeMultiplier } """ .trimIndent() internal companion object { // Setting the default to 0 indicates that there is no max. public const val DEFAULT_MAX_FONT_SIZE_MULTIPLIER: Float = 0.0f } }