Avoid crashing when measuring large text
In: AnnotatedString, String, TextMeasurer paths
fixes: b/341928267
Test: ./gradlew :com:found:found:cAT
Test: ./gradlew :com:ui:ui-text:cAT
(cherry picked from https://android-review.googlesource.com/q/commit:882f214e40dadd753901e69912ee954c65bca726)
(cherry picked from https://android-review.googlesource.com/q/commit:074f0c123338c7ef6794a33dbd05865d98fdecc9)
Merged-In: I7c067c1d2b90484e8fe1f596bf04f84b1ffc0c1a
Change-Id: I7c067c1d2b90484e8fe1f596bf04f84b1ffc0c1a
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheTest.kt
index 6a51fca..38232c7 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheTest.kt
@@ -340,4 +340,17 @@
)
assertThat(actual.height).isEqualTo(expected.height)
}
+
+ @Test
+ fun hugeString_doesntCrash() {
+ val text = "A".repeat(100_000)
+ val subject =
+ MultiParagraphLayoutCache(
+ text = AnnotatedString(text),
+ style = TextStyle(fontSize = 100.sp),
+ fontFamilyResolver = fontFamilyResolver,
+ )
+ .also { it.density = density }
+ subject.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
+ }
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCacheTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCacheTest.kt
index f38a424..a735780 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCacheTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCacheTest.kt
@@ -343,6 +343,15 @@
assertThat(subject.slowCreateTextLayoutResultOrNull(style = style)).isNotNull()
}
+ @Test
+ fun hugeString_doesntCrash() {
+ val text = "A".repeat(100_000)
+ val style = createTextStyle(fontSize = 100.sp)
+ val subject =
+ ParagraphLayoutCache(text, style, fontFamilyResolver).also { it.density = density }
+ subject.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
+ }
+
private fun createTextStyle(
fontSize: TextUnit,
letterSpacing: TextUnit = TextUnit.Unspecified
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
index 201baaf..faa55b3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
@@ -28,7 +28,8 @@
softWrap: Boolean,
overflow: TextOverflow,
maxIntrinsicWidth: Float
-): Constraints = Constraints(
+): Constraints =
+ Constraints.fitPrioritizingWidth(
minWidth = 0,
maxWidth = finalMaxWidth(constraints, softWrap, overflow, maxIntrinsicWidth),
minHeight = 0,
diff --git a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt
index 404da84..ee33aa8 100644
--- a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt
+++ b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/TextMeasurerTest.kt
@@ -348,6 +348,12 @@
.isEqualTo(TextDecoration.LineThrough)
}
+ @Test
+ fun emptyConstraints_hugeString_dontCrash() {
+ val subject = textMeasurer()
+ subject.measure("A".repeat(100_000), TextStyle.Default)
+ }
+
private fun textLayoutInput(
text: AnnotatedString = AnnotatedString("Hello"),
style: TextStyle = TextStyle.Default,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
index 533d7eb..5d139e3 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
@@ -282,8 +282,9 @@
// This is a fallback behavior because native text layout doesn't support multiple
// ellipsis in one text layout.
- // When softWrap is turned off and overflow is ellipsis, it's expected that each line
- // that exceeds maxWidth will be ellipsized.
+ // When softWrap is turned off and overflow is ellipsis, it's expected that each
+ // line that exceeds maxWidth will be ellipsized.
+ //
// For example,
// input text:
// "AAAA\nAAAA"
@@ -304,7 +305,8 @@
// width to be passed to Paragraph
// if max intrinsic width is between minWidth and maxWidth
// we can use it to layout
- // else if max intrinsic width is greater than maxWidth, we can only use maxWidth
+ // else if max intrinsic width is greater than maxWidth, we can only use
+ // maxWidth
// else if max intrinsic width is less than minWidth, we should use minWidth
val width = if (minWidth == maxWidth) {
maxWidth
@@ -314,7 +316,12 @@
val multiParagraph = MultiParagraph(
intrinsics = nonNullIntrinsics,
- constraints = Constraints(maxWidth = width, maxHeight = constraints.maxHeight),
+ constraints = Constraints.fitPrioritizingWidth(
+ minWidth = 0,
+ maxWidth = width,
+ minHeight = 0,
+ maxHeight = constraints.maxHeight
+ ),
// This is a fallback behavior for ellipsis. Native
maxLines = finalMaxLines,
ellipsis = overflow == TextOverflow.Ellipsis