Skip to content

Instantly share code, notes, and snippets.

@CanYumusak
Last active September 22, 2023 19:54
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CanYumusak/34e6620f444d5ba0c8f7419362d5d394 to your computer and use it in GitHub Desktop.
Save CanYumusak/34e6620f444d5ba0c8f7419362d5d394 to your computer and use it in GitHub Desktop.
LabelLayoutModifier which adjusts Android text placement to match Figma text placement
public class LabelLayoutModifier(
val context: Context,
val lineHeight: TextUnit,
val style: MyTextStyle,
) : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val placeable = measurable.measure(constraints)
val lineCount = lineCount(placeable)
val fullHeight = (lineHeight.toPx() * lineCount).roundToInt()
val fontMetrics = fontMetrics(context, style)
val centerOffset = floor((lineHeight.toPx().toDp() - fontMetrics.descent.toDp() + fontMetrics.ascent.toDp()).value / 2f).dp.toPx().toInt()
val figmaOffset = fontMetrics.ascent - fontMetrics.top
return layout(width = placeable.width, height = fullHeight) {
// Alignment lines are recorded with the parents automatically.
placeable.placeRelative(
x = 0,
y = (centerOffset - figmaOffset).toInt()
)
}
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
): Int {
return ceilToLineHeight(measurable.maxIntrinsicHeight(width))
}
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
): Int {
return ceilToLineHeight(measurable.minIntrinsicHeight(width))
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
): Int {
return measurable.minIntrinsicWidth(height)
}
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
): Int {
return measurable.maxIntrinsicWidth(height)
}
private fun Density.lineCount(placeable: Placeable): Int {
val firstToLast = (placeable[LastBaseline] - placeable[FirstBaseline]).toFloat()
return (firstToLast / lineHeight.toPx()).roundToInt() + 1
}
private fun Density.ceilToLineHeight(value: Int): Int {
val lineHeightPx = lineHeight.toPx()
return (ceil(value.toFloat() / lineHeightPx) * lineHeightPx).roundToInt()
}
}
private fun Density.fontMetrics(context: Context, textStyle: MyTextStyle): Paint.FontMetrics {
val fontResourceId = textStyle.fonts[textStyle.fontWeight]!!
val font = ResourcesCompat.getFont(context, fontResourceId)
val paint = Paint().also {
it.typeface = font
it.textSize = textStyle.fontSize.toPx()
}
return paint.fontMetrics
}
public data class MyTextStyle internal constructor(
val fontSize: TextUnit,
val fontWeight: FontWeight,
val letterSpacing: TextUnit,
val lineHeight: TextUnit,
) {
public val fonts: Map<FontWeight, Int> = mapOf(
WaveFontWeight.Demi.fontWeight to R.font.nationale_demi_bold,
WaveFontWeight.Bold.fontWeight to R.font.nationale_bold,
)
private val fontFamily: FontFamily = FontFamily(
fonts.map { Font(it.value, it.key) }
)
internal fun asTextStyle(): TextStyle {
return TextStyle(
fontSize = fontSize,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
lineHeight = lineHeight,
)
}
}
@kikin81
Copy link

kikin81 commented Sep 19, 2023

@CanYumusak I was wondering if updating to latest compose with font padding disabled helped you?
I updated to compose 1.5.1 and disabled font padding but I'm still not matching figma specs.
My text style:

        platformStyle = PlatformTextStyle(
            includeFontPadding = false,
        ),
        lineHeightStyle = LineHeightStyle(
            alignment = LineHeightStyle.Alignment.Center,
            trim = LineHeightStyle.Trim.None,
        ),

I have tried different combinations of LineHeightStyle.Alignment and LineHeightStyle.Trim but it's off by a couple of pixels

CleanShot 2023-09-19 at 14 34 06

@CanYumusak
Copy link
Author

Heyhey,

an important detail with that change is to set the line-height to the correct value specified in Figma. The intrinsic calculation of the LineHeight does not render the same value that Figma gets at.
So please make sure to set the same height.

PS: It also seems like your letterspacing is off

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment