
androidAndroidTest.androidx.compose.ui.text.MultiParagraphIntegrationTest.kt Maven / Gradle / Ivy
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.ui.text
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathOperation
import androidx.compose.ui.text.AnnotatedString.Range
import androidx.compose.ui.text.FontTestData.Companion.BASIC_MEASURE_FONT
import androidx.compose.ui.text.font.toFontFamily
import androidx.compose.ui.text.intl.LocaleList
import androidx.compose.ui.text.matchers.assertThat
import androidx.compose.ui.text.matchers.isZero
import androidx.compose.ui.text.style.ResolvedTextDirection
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SmallTest
class MultiParagraphIntegrationTest {
private val fontFamilyMeasureFont = BASIC_MEASURE_FONT.toFontFamily()
private val context = InstrumentationRegistry.getInstrumentation().context
private val defaultDensity = Density(density = 1f)
private val ltrLocaleList = LocaleList("en")
private val rtlLocaleList = LocaleList("ar")
@Test
fun didExceedMaxLines_withLineFeed() {
// The text should be rendered with 3 lines:
// a
// b
// c
val text = createAnnotatedString("a\nb", "c")
// maxLines be 1 or 2, smaller than the line count 3
for (i in 1..2) {
val paragraph = simpleMultiParagraph(
text = text,
maxLines = i
)
assertWithMessage("text has 3 lines, maxLines = $i")
.that(paragraph.didExceedMaxLines).isTrue()
}
// maxLines be 3, 4, 5 larger than the line count 3
for (i in 3..5) {
val paragraph = simpleMultiParagraph(
text = text,
maxLines = i
)
assertWithMessage("text has 3 lines, maxLines = $i")
.that(paragraph.didExceedMaxLines).isFalse()
}
}
@Test
fun didExceedMaxLines_withLineWrap() {
with(defaultDensity) {
val fontSize = 50.sp
// Each line has the space only for 1 character
val width = fontSize.toPx()
// The text should be rendered with 3 lines:
// a
// b
// c
val text = createAnnotatedString("ab", "c")
for (i in 1..2) {
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
maxLines = i,
width = width
)
assertWithMessage("text has 3 lines, maxLines = $i")
.that(paragraph.didExceedMaxLines).isTrue()
}
for (i in 3..5) {
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
maxLines = i
)
assertWithMessage("text has 3 lines, maxLines = $i")
.that(paragraph.didExceedMaxLines).isFalse()
}
}
}
@Test
fun textOverflow_exceedMaxLines_singleParagraph() {
val text = createAnnotatedString("a\nb")
val paragraph = simpleMultiParagraph(text = text, maxLines = 1)
assertThat(paragraph.paragraphInfoList[0].paragraph.didExceedMaxLines).isTrue()
}
@Test
fun textOverflow_exceedMaxLinesInMiddle_multiParagraph() {
val text = createAnnotatedString("a\nb", "a\nb")
val paragraph = simpleMultiParagraph(text = text, maxLines = 3)
assertThat(paragraph.paragraphInfoList[1].paragraph.didExceedMaxLines).isTrue()
}
@Test
fun textOverflow_exceedMaxLinesInGap_multiParagraph() {
val text = createAnnotatedString("a\nb", "a")
val paragraph = simpleMultiParagraph(text = text, maxLines = 2)
assertThat(paragraph.paragraphInfoList.size).isEqualTo(1)
}
@Test
fun getPathForRange() {
with(defaultDensity) {
val text = createAnnotatedString("ab", "c", "de")
val fontSize = 20.sp
val fontSizeInPx = fontSize.toPx()
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize
)
// Select "bcd"
val actualPath = paragraph.getPathForRange(1, 4)
val expectedPath = Path()
// path covering "b"
expectedPath.addRect(
Rect(fontSizeInPx, 0f, fontSizeInPx * 2, fontSizeInPx)
)
// path covering "c"
expectedPath.addRect(
Rect(0f, fontSizeInPx, fontSizeInPx, fontSizeInPx * 2)
)
// path covering "d"
expectedPath.addRect(
Rect(0f, fontSizeInPx * 2, fontSizeInPx, fontSizeInPx * 3)
)
val diff = Path.combine(PathOperation.Difference, expectedPath, actualPath).getBounds()
assertThat(diff).isEqualTo(Rect.Zero)
}
}
@Test(expected = IllegalArgumentException::class)
fun getPathForRange_throws_exception_if_start_larger_than_end() {
val text = "abc"
val textStart = 0
val textEnd = text.length
val paragraph = simpleMultiParagraph(text = text)
paragraph.getPathForRange(textEnd, textStart)
}
@Test(expected = IllegalArgumentException::class)
fun getPathForRange_throws_exception_if_start_is_smaller_than_zero() {
val text = "abc"
val textStart = 0
val textEnd = text.length
val paragraph = simpleMultiParagraph(text = text)
paragraph.getPathForRange(textStart - 2, textEnd - 1)
}
@Test(expected = IllegalArgumentException::class)
fun getPathForRange_throws_exception_if_end_is_larger_than_text_length() {
val text = "abc"
val textStart = 0
val textEnd = text.length
val paragraph = simpleMultiParagraph(text = text)
paragraph.getPathForRange(textStart, textEnd + 1)
}
@Test
fun getOffsetForPosition() {
with(defaultDensity) {
val lineLength = 2
val text = createAnnotatedString(List(3) { "a".repeat(lineLength) })
val fontSize = 50.sp
val fontSizeInPx = fontSize.roundToPx()
// each line contains 2 character
val width = 2 * fontSizeInPx
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
width = width.toFloat()
)
// The text should be rendered as:
// aa
// aa
// aa
for (i in 0 until text.length) {
val row = i / lineLength
val y = fontSizeInPx / 2 + fontSizeInPx * row
val col = i % lineLength
val x = fontSizeInPx * col
val actualOffset = paragraph.getOffsetForPosition(
Offset(x.toFloat(), y.toFloat())
)
assertWithMessage("getOffsetForPosition($x, $y) failed")
.that(actualOffset).isEqualTo(i)
}
}
}
@Test
fun getBoundingBox() {
with(defaultDensity) {
val lineLength = 2
val text = createAnnotatedString(List(3) { "a".repeat(lineLength) })
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
width = text.length * fontSizeInPx
)
// The text should be rendered as:
// aa
// aa
// aa
for (i in 0 until text.length) {
val row = i / lineLength
val col = i % lineLength
val expectedBox = Rect(
left = col * fontSizeInPx,
right = (col + 1) * fontSizeInPx,
top = row * fontSizeInPx,
bottom = (row + 1) * fontSizeInPx
)
val actualBox = paragraph.getBoundingBox(i)
assertWithMessage("getBoundingBox($i) failed")
.that(actualBox).isEqualTo(expectedBox)
}
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getBoundingBox_offset_negative() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getBoundingBox(-1)
}
@Suppress
@Test(expected = java.lang.IllegalArgumentException::class)
fun getBoundingBox_offset_larger_than_length_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getBoundingBox(text.length + 1)
}
@Test
fun getHorizontalPosition() {
with(defaultDensity) {
val paragraphCount = 3
val lineLength = 2
val text = createAnnotatedString(List(paragraphCount) { "a".repeat(lineLength) })
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize
)
for (i in 0 until text.length) {
val col = i % lineLength
val expectPos = fontSizeInPx * col
val actualPos = paragraph.getHorizontalPosition(i, true)
assertWithMessage("getHorizontalPosition($i) failed")
.that(actualPos).isEqualTo(expectPos)
}
val expectPos = fontSizeInPx * lineLength
val actualPos = paragraph.getHorizontalPosition(text.length, true)
assertWithMessage("getHorizontalPosition(${text.length}) failed")
.that(actualPos).isEqualTo(expectPos)
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getHorizontalPosition_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getHorizontalPosition(-1, true)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getHorizontalPosition_larger_than_length_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getHorizontalPosition(text.length + 1, true)
}
@Test
fun getParagraphDirection_textDirection_Default() {
val text = createAnnotatedString("a", "\u05D0", " ")
val paragraph = simpleMultiParagraph(text = text, localeList = ltrLocaleList)
assertThat(paragraph.getParagraphDirection(0)).isEqualTo(ResolvedTextDirection.Ltr)
assertThat(paragraph.getParagraphDirection(1)).isEqualTo(ResolvedTextDirection.Rtl)
assertThat(paragraph.getParagraphDirection(2)).isEqualTo(ResolvedTextDirection.Ltr)
assertThat(paragraph.getParagraphDirection(3)).isEqualTo(ResolvedTextDirection.Ltr)
}
@Test
fun getParagraphDirection_textDirection_Content_withLtrLocale() {
val text = createAnnotatedString("a", "\u05D0", " ")
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(
textDirection = TextDirection.Content,
localeList = ltrLocaleList
)
)
assertThat(paragraph.getParagraphDirection(0)).isEqualTo(ResolvedTextDirection.Ltr)
assertThat(paragraph.getParagraphDirection(1)).isEqualTo(ResolvedTextDirection.Rtl)
assertThat(paragraph.getParagraphDirection(2)).isEqualTo(ResolvedTextDirection.Ltr)
assertThat(paragraph.getParagraphDirection(3)).isEqualTo(ResolvedTextDirection.Ltr)
}
@Test
fun getParagraphDirection_textDirection_Content_withRtlLocale() {
val text = createAnnotatedString("a", "\u05D0", " ")
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(
textDirection = TextDirection.Content,
localeList = rtlLocaleList
)
)
assertThat(paragraph.getParagraphDirection(0)).isEqualTo(ResolvedTextDirection.Ltr)
assertThat(paragraph.getParagraphDirection(1)).isEqualTo(ResolvedTextDirection.Rtl)
assertThat(paragraph.getParagraphDirection(2)).isEqualTo(ResolvedTextDirection.Rtl)
assertThat(paragraph.getParagraphDirection(3)).isEqualTo(ResolvedTextDirection.Rtl)
}
@Test
fun getParagraphDirection_textDirection_ForceLtr() {
val text = createAnnotatedString("a", "\u05D0", " ")
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(
textDirection = TextDirection.Ltr
)
)
for (i in 0..text.length) {
assertWithMessage("getParagraphDirection($i) failed")
.that(paragraph.getParagraphDirection(i)).isEqualTo(ResolvedTextDirection.Ltr)
}
}
@Test
fun getParagraphDirection_textDirection_ForceRtl() {
val text = createAnnotatedString("a", "\u05D0", " ")
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(
textDirection = TextDirection.Rtl
)
)
for (i in 0..text.length) {
assertWithMessage("getParagraphDirection($i) failed")
.that(paragraph.getParagraphDirection(i)).isEqualTo(ResolvedTextDirection.Rtl)
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getParagraphDirection_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getParagraphDirection(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getParagraphDirection_larger_than_length_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getParagraphDirection(text.length + 1)
}
@Test
fun getBidiRunDirection() {
val text = createAnnotatedString("a\u05D0", "\u05D0a")
val paragraph = simpleMultiParagraph(text = text)
assertThat(paragraph.getBidiRunDirection(0)).isEqualTo(ResolvedTextDirection.Ltr)
assertThat(paragraph.getBidiRunDirection(1)).isEqualTo(ResolvedTextDirection.Rtl)
assertThat(paragraph.getBidiRunDirection(2)).isEqualTo(ResolvedTextDirection.Rtl)
assertThat(paragraph.getBidiRunDirection(3)).isEqualTo(ResolvedTextDirection.Ltr)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getBidiRunDirection_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getBidiRunDirection(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getBidiRunDirection_larger_than_length_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getBidiRunDirection(text.length + 1)
}
@Test
fun getWordBoundary() {
val text = createAnnotatedString("ab cd", "e f")
val paragraph = simpleMultiParagraph(text = text)
val textString = text.text
assertThat(paragraph.getWordBoundary(textString.indexOf('a')))
.isEqualTo(
TextRange(
textString.indexOf('a'),
textString.indexOf('b') + 1
)
)
assertThat(paragraph.getWordBoundary(textString.indexOf('d')))
.isEqualTo(
TextRange(
textString.indexOf('c'),
textString.indexOf('d') + 1
)
)
assertThat(paragraph.getWordBoundary(textString.indexOf('e')))
.isEqualTo(
TextRange(
textString.indexOf('e'),
textString.indexOf('e') + 1
)
)
assertThat(paragraph.getWordBoundary(textString.indexOf('f')))
.isEqualTo(
TextRange(
textString.indexOf('f'),
textString.indexOf('f') + 1
)
)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getWordBoundary_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getWordBoundary(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getWordBoundary_larger_than_length_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getWordBoundary(text.length + 1)
}
@Test
fun getCursorRect() {
with(defaultDensity) {
val paragraphCount = 3
val lineLength = 2
// A text with 3 lines and each line has 2 characters.
val text = createAnnotatedString(List(paragraphCount) { "a".repeat(lineLength) })
val fontSize = 10.sp
val fontSizeInPx = fontSize.toPx()
val width = 2 * fontSizeInPx
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
width = width
)
for (i in 0 until text.length) {
val row = i / lineLength
val col = i % lineLength
val top = row * fontSizeInPx
val cursorXOffset = col * fontSizeInPx
val expectRect = Rect(
left = cursorXOffset,
top = top,
right = cursorXOffset,
bottom = top + fontSizeInPx
)
val actualRect = paragraph.getCursorRect(i)
assertWithMessage("getCursorRect($i) failed")
.that(actualRect).isEqualTo(expectRect)
}
// Last cursor position is the end of the last line.
val row = paragraph.lineCount - 1
val col = lineLength
val top = row * fontSizeInPx
val cursorXOffset = col * fontSizeInPx
val expectRect = Rect(
left = cursorXOffset,
top = top,
right = cursorXOffset,
bottom = top + fontSizeInPx
)
val actualRect = paragraph.getCursorRect(text.length)
assertWithMessage("getCursorRect(${text.length}) failed")
.that(actualRect).isEqualTo(expectRect)
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getCursorRect_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getCursorRect(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getCursorRect_larger_than_length_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getCursorRect(text.length + 1)
}
@Test
fun getLineForOffset() {
val text = createAnnotatedString("a", "a\na")
val paragraph = simpleMultiParagraph(text = text)
assertThat(paragraph.getLineForOffset(0)).isEqualTo(0)
assertThat(paragraph.getLineForOffset(1)).isEqualTo(1)
// '\n' is not checked because it's Paragraph's implementation
assertThat(paragraph.getLineForOffset(3)).isEqualTo(2)
}
@Test
fun getLineForOffset_negative_returnsZero() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
assertThat(paragraph.getLineForOffset(-1)).isZero()
}
@Test
fun getLineForOffset_larger_than_length_returnsLastLine() {
val text = "abc\ndef"
val paragraph = simpleMultiParagraph(text = text)
assertThat(paragraph.getLineForOffset(text.length + 1))
.isEqualTo(1)
}
@Test
fun getLineLeft() {
with(defaultDensity) {
val text = createAnnotatedString("aa", "\u05D0\u05D0")
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val width = simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth * 2
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
width = width
)
assertThat(paragraph.getLineLeft(0)).isEqualTo(0)
assertThat(paragraph.getLineLeft(1)).isEqualTo(width - 2 * fontSizeInPx)
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineLeft_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineLeft(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineLeft_greaterThanOrEqual_lineCount_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineLeft(paragraph.lineCount)
}
@Test
fun lineContentForEmptyText() {
val text = ""
val paragraph = simpleMultiParagraph(text = text)
assertThat(paragraph.lineCount).isEqualTo(1)
assertThat(paragraph.getLineStart(0)).isEqualTo(0)
assertThat(paragraph.getLineEnd(0)).isEqualTo(0)
assertThat(paragraph.getLineLeft(0)).isEqualTo(0)
assertThat(paragraph.getLineRight(0)).isEqualTo(0)
assertThat(paragraph.getLineEnd(0)).isEqualTo(0)
assertThat(paragraph.getLineEnd(0, true)).isEqualTo(0)
assertThat(paragraph.isLineEllipsized(0)).isFalse()
}
@Test
fun getLineRight() {
with(defaultDensity) {
val text = createAnnotatedString("aa", "\u05D0\u05D0")
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val width = simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth * 2
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
width = width
)
assertThat(paragraph.getLineRight(0)).isEqualTo(2 * fontSizeInPx)
assertThat(paragraph.getLineRight(1)).isEqualTo(width)
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineRight_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineRight(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineRight_greaterThanOrEqual_lineCount_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineRight(paragraph.lineCount)
}
@Test
fun getLineTop() {
with(defaultDensity) {
val text = createAnnotatedString("a", "a", "a")
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize
)
for (i in 0 until paragraph.lineCount) {
assertWithMessage("bottom of line $i doesn't match")
.that(paragraph.getLineTop(i))
.isEqualTo(fontSizeInPx * i)
}
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineTop_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineTop(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineTop_greaterThanOrEqual_lineCount_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineTop(paragraph.lineCount)
}
@Test
fun getLineBottom() {
with(defaultDensity) {
val text = createAnnotatedString("a", "a", "a")
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize
)
for (i in 0 until paragraph.lineCount) {
assertWithMessage("bottom of line $i doesn't match")
.that(paragraph.getLineBottom(i))
.isEqualTo(fontSizeInPx * (i + 1))
}
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineBottom_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineBottom(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineBottom_greaterThanOrEqual_lineCount_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineBottom(paragraph.lineCount)
}
@Test
fun getLineHeight() {
with(defaultDensity) {
val text = createAnnotatedString("a", "a", "a")
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize
)
for (i in 0 until paragraph.lineCount) {
assertWithMessage("getLineHeight($i) failed")
.that(paragraph.getLineHeight(i)).isEqualTo(fontSizeInPx)
}
}
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineHeight_negative_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineHeight(-1)
}
@Test(expected = java.lang.IllegalArgumentException::class)
fun getLineHeight_greaterThanOrEqual_lineCount_throw_exception() {
val text = "abc"
val paragraph = simpleMultiParagraph(text = text)
paragraph.getLineHeight(paragraph.lineCount)
}
@Test
fun textAlign_defaultValue_alignsStart() {
val textLtr = "aa"
val textRtl = "\u05D0\u05D0"
val text = createAnnotatedString(textLtr, textRtl)
// Width should be sufficient to make each paragraph one line.
val width = 2 * simpleMultiParagraphIntrinsics(text).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(text = text, width = width)
// When text align to start, Ltr text aligns to left, line left should be 0.
assertThat(paragraph.getLineLeft(0)).isZero()
// When text align to start, Rtl text aligns to right, line right should be width.
assertThat(paragraph.getLineRight(1)).isEqualTo(width)
}
@Test
fun textAlign_left_returnsZeroForGetLineLeft() {
val textLtr = "aa"
val textRtl = "\u05D0\u05D0"
val text = createAnnotatedString(textLtr, textRtl)
// Width should be sufficient to make each paragraph one line.
val width = 2 * simpleMultiParagraphIntrinsics(text).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
width = width,
style = TextStyle(textAlign = TextAlign.Left)
)
// When text align to left, line left should be 0 for both Ltr and Rtl text.
assertThat(paragraph.getLineLeft(0)).isZero()
assertThat(paragraph.getLineLeft(1)).isZero()
}
@Test
fun textAlign_right_returnsWidthForGetLineRight() {
val textLtr = "aa"
val textRtl = "\u05D0\u05D0"
val text = createAnnotatedString(textLtr, textRtl)
// Width should be sufficient to make each paragraph one line.
val width = 2 * simpleMultiParagraphIntrinsics(text).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
width = width,
style = TextStyle(textAlign = TextAlign.Right)
)
// When text align to right, line right should be width for both Ltr and Rtl text.
assertThat(paragraph.getLineRight(0)).isEqualTo(width)
assertThat(paragraph.getLineRight(1)).isEqualTo(width)
}
@Test
fun textAlign_center_textIsCentered() {
with(defaultDensity) {
val textLtr = "aa"
val textRtl = "\u05D0\u05D0"
val text = createAnnotatedString(textLtr, textRtl)
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
// Width should be sufficient to make each paragraph one line.
val width = 2 * simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
width = width,
style = TextStyle(textAlign = TextAlign.Center)
)
val expectedLineLeft = width / 2 - (fontSizeInPx * textLtr.length) / 2
val expectedLineRight = width / 2 + (fontSizeInPx * textLtr.length) / 2
assertThat(paragraph.getLineLeft(0)).isEqualTo(expectedLineLeft)
assertThat(paragraph.getLineRight(0)).isEqualTo(expectedLineRight)
assertThat(paragraph.getLineLeft(1)).isEqualTo(expectedLineLeft)
assertThat(paragraph.getLineRight(1)).isEqualTo(expectedLineRight)
}
}
@Test
@SdkSuppress(minSdkVersion = 28)
// We have to test strict justification above API 28 because of this bug b/68009059, where
// devices before API 28 may have an extra space at the end of line.
fun textAlign_justify_justifies() {
val textLtr = "a a a"
val textRtl = "\u05D0 \u05D0 \u05D0"
val text = createAnnotatedString(textLtr, textRtl)
// Justify only works for soft wrapped lines, so width is made insufficient.
val width = simpleMultiParagraphIntrinsics(text).maxIntrinsicWidth - 1f
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(textAlign = TextAlign.Justify),
width = width
)
// When text is justified, line left is 0 while line right is width
assertThat(paragraph.getLineLeft(0)).isZero()
assertThat(paragraph.getLineRight(0)).isEqualTo(width)
assertThat(paragraph.getLineLeft(2)).isZero()
assertThat(paragraph.getLineRight(2)).isEqualTo(width)
}
@Test
@SdkSuppress(maxSdkVersion = 27, minSdkVersion = 26)
fun textAlign_justify_justifies_underApi28() {
with(defaultDensity) {
val textLtr = "a a a"
val textRtl = "\u05D0 \u05D0 \u05D0"
val text = createAnnotatedString(textLtr, textRtl)
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
// Justify only works for soft wrapped lines, so width is made insufficient.
val width = simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth - 1f
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
style = TextStyle(textAlign = TextAlign.Justify),
width = width
)
// When Ltr text is justified, line left is 0.
assertThat(paragraph.getLineLeft(0)).isZero()
// When Ltr text is justified, line right is greater than when it's align left. We
// can only assert a weaker condition due to bug b/68009059, where extra space is
// added at the end of the line.
assertThat(paragraph.getLineRight(0))
.isGreaterThan("a a".length * fontSizeInPx)
// When Rtl text is justified, line right is width.
assertThat(paragraph.getLineRight(2)).isEqualTo(width)
// Similar to Ltr text, when Rtl text is justified, line left is less than when it's
// align right.
assertThat(paragraph.getLineLeft(2))
.isLessThan(width - "\u05D0 \u05D0".length * fontSizeInPx)
}
}
@Test
fun textAlign_start_alignsStart() {
val textLtr = "aa"
val textRtl = "\u05D0\u05D0"
val text = createAnnotatedString(textLtr, textRtl)
// Width should be sufficient to make each paragraph one line.
val width = 2 * simpleMultiParagraphIntrinsics(text).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(textAlign = TextAlign.Start),
width = width
)
// When text align to start, Ltr text aligns to left, line left should be 0.
assertThat(paragraph.getLineLeft(0)).isZero()
// When text align to start, Rtl text aligns to right, line right should be width.
assertThat(paragraph.getLineRight(1)).isEqualTo(width)
}
@Test
fun textAlign_end_alignsEnd() {
val textLtr = "aa"
val textRtl = "\u05D0\u05D0"
val text = createAnnotatedString(textLtr, textRtl)
// Width should be sufficient to make each paragraph one line.
val width = 2 * simpleMultiParagraphIntrinsics(text).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(textAlign = TextAlign.End),
width = width
)
// When text align to start, Ltr text aligns to right, line right should be width.
assertThat(paragraph.getLineRight(0)).isEqualTo(width)
// When text align to start, Rtl text aligns to left, line left should 0.
assertThat(paragraph.getLineLeft(1)).isZero()
}
@Test
fun textDirection_content_withLtrLocale() {
with(defaultDensity) {
val textLtr = "a ."
val textRtl = "\u05D0 ."
val textNeutral = " ."
val text = createAnnotatedString(textLtr, textRtl, textNeutral)
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val width = simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
style = TextStyle(
textDirection = TextDirection.Content,
localeList = ltrLocaleList
),
width = width
)
// First paragraph should be rendered as: "a .", dot is visually after "a ".
assertThat(paragraph.getHorizontalPosition(2, true))
.isEqualTo("a ".length * fontSizeInPx)
// Second paragraph should be rendered as: ". א", dot is visually before " א".
assertThat(paragraph.getHorizontalPosition(5, true))
.isEqualTo(width - "\u05D0 ".length * fontSizeInPx)
// Third paragraph should be rendered as: " .", dot is visually after " ".
assertThat(paragraph.getHorizontalPosition(8, true))
.isEqualTo(" ".length * fontSizeInPx)
}
}
@Test
fun textDirection_content_withRtlLocale() {
with(defaultDensity) {
val textLtr = "a ."
val textRtl = "\u05D0 ."
val textNeutral = " ."
val text = createAnnotatedString(textLtr, textRtl, textNeutral)
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val width = simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
style = TextStyle(
textDirection = TextDirection.Content,
localeList = rtlLocaleList
),
width = width
)
// First paragraph should be rendered as: "a .", dot is visually after "a ".
assertThat(paragraph.getHorizontalPosition(2, true))
.isEqualTo("a ".length * fontSizeInPx)
// Second paragraph should be rendered as: ". א", dot is visually before " א".
assertThat(paragraph.getHorizontalPosition(5, true))
.isEqualTo(width - "\u05D0 ".length * fontSizeInPx)
// Third paragraph should be rendered as: ". ", dot is visually before " ".
assertThat(paragraph.getHorizontalPosition(8, true))
.isEqualTo(width - " ".length * fontSizeInPx)
}
}
@Test
fun textDirection_forceLtr() {
with(defaultDensity) {
val textLtr = "a ."
val textRtl = "\u05D0 ."
val textNeutral = " ."
val text = createAnnotatedString(textLtr, textRtl, textNeutral)
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val width = simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
style = TextStyle(textDirection = TextDirection.Ltr),
width = width
)
// First paragraph should be rendered as: "a .", dot is visually after "a ".
assertThat(paragraph.getHorizontalPosition(2, true))
.isEqualTo("a ".length * fontSizeInPx)
// Second paragraph should be rendered as: "א .", dot is visually after "א ".
assertThat(paragraph.getHorizontalPosition(5, true))
.isEqualTo("\u05D0 ".length * fontSizeInPx)
// Third paragraph should be rendered as: " .", dot is visually after " ".
assertThat(paragraph.getHorizontalPosition(8, true))
.isEqualTo(" ".length * fontSizeInPx)
}
}
@Test
fun textDirection_forceRtl() {
with(defaultDensity) {
val textLtr = "a ."
val textRtl = "\u05D0 ."
val textNeutral = " ."
val text = createAnnotatedString(textLtr, textRtl, textNeutral)
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
val width = simpleMultiParagraphIntrinsics(text, fontSize).maxIntrinsicWidth
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
style = TextStyle(textDirection = TextDirection.Rtl),
width = width
)
// First paragraph should be rendered as: ". a", dot is visually before " a".
assertThat(paragraph.getHorizontalPosition(2, true))
.isEqualTo(width - "a ".length * fontSizeInPx)
// Second paragraph should be rendered as: ". א", dot is visually before " א".
assertThat(paragraph.getHorizontalPosition(5, true))
.isEqualTo(width - "\u05D0 ".length * fontSizeInPx)
// Third paragraph should be rendered as: ". ", dot is visually before " ".
assertThat(paragraph.getHorizontalPosition(8, true))
.isEqualTo(width - " ".length * fontSizeInPx)
}
}
@Test
fun lineHeight_returnsSameAsGiven() {
with(defaultDensity) {
val text = createAnnotatedString("a\na\na", "a\na\na")
// Need to specify font size in case the asserted line height happens to be the default
// line height corresponding to the font size.
val fontSize = 50.sp
val lineHeight = 80.sp
val lineHeightInPx = lineHeight.toPx()
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
style = TextStyle(lineHeight = lineHeight)
)
// Height of first and last line in each paragraph is influenced by includePadding.
// So we only assert the inner paragraph lines' height.
assertThat(paragraph.getLineHeight(1)).isEqualTo(lineHeightInPx)
assertThat(paragraph.getLineHeight(4)).isEqualTo(lineHeightInPx)
}
}
@Test
fun drawText_withUnderlineStyle_equalToUnderlinePaint() = with(defaultDensity) {
val fontSize = 30.sp
val fontSizeInPx = fontSize.toPx()
val multiParagraph = simpleMultiParagraph(
text = buildAnnotatedString {
withStyle(SpanStyle(textDecoration = TextDecoration.Underline)) {
append("レンズ(単焦点)")
}
},
style = TextStyle(fontSize = fontSize),
width = fontSizeInPx * 20
)
val multiParagraph2 = simpleMultiParagraph(
text = AnnotatedString("レンズ(単焦点)"),
style = TextStyle(
fontSize = fontSize,
textDecoration = TextDecoration.Underline
),
width = fontSizeInPx * 20
)
val bitmapWithSpan = multiParagraph.bitmap()
val bitmapNoSpan = multiParagraph2.bitmap()
assertThat(bitmapWithSpan).isEqualToBitmap(bitmapNoSpan)
}
@Test
fun textIndent_onFirstLine() {
with(defaultDensity) {
val text = createAnnotatedString("aaa", "\u05D0\u05D0\u05D0")
val indent = 20.sp
val indentInPx = indent.toPx()
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
// Width is the space needed by 2 characters
val width = 2 * fontSizeInPx
val paragraph = simpleMultiParagraph(
text = text,
style = TextStyle(textIndent = TextIndent(firstLine = indent)),
fontSize = fontSize,
width = width
)
// The paragraph should be rendered as:
// a
// aa
// א
// אא
assertThat(paragraph.getHorizontalPosition(0, true)).isEqualTo(indentInPx)
assertThat(paragraph.getHorizontalPosition(1, true)).isZero()
assertThat(paragraph.getHorizontalPosition(3, true)).isEqualTo(width - indentInPx)
assertThat(paragraph.getHorizontalPosition(4, true)).isEqualTo(width)
}
}
@Test
@MediumTest
fun textIndent_onRestLine() {
with(defaultDensity) {
val text = createAnnotatedString("aaa", "\u05D0\u05D0\u05D0")
val indent = 20.sp
val indentInPx = indent.toPx()
val fontSize = 50.sp
val fontSizeInPx = fontSize.toPx()
// Width is the space needed by 2 characters
val width = 2 * fontSizeInPx
val paragraph = simpleMultiParagraph(
text = text,
fontSize = fontSize,
style = TextStyle(textIndent = TextIndent(restLine = indent)),
width = width
)
// The paragraph should be rendered as:
// aa
// a
// אא
// א
assertThat(paragraph.getHorizontalPosition(0, true)).isZero()
assertThat(paragraph.getHorizontalPosition(2, true)).isEqualTo(indentInPx)
assertThat(paragraph.getHorizontalPosition(3, true)).isEqualTo(width)
assertThat(paragraph.getHorizontalPosition(5, true)).isEqualTo(width - indentInPx)
}
}
@Test
fun annotatedString_haveParagraphStyle_withoutTextDirection() {
// Provide an LTR text
val text = AnnotatedString(
text = "ab",
paragraphStyles = listOf(
Range(
item = ParagraphStyle(textDirection = TextDirection.Content),
start = 0,
end = "a".length
),
Range(
// skip setting [TextDirection] on purpose, should inherit from the
// main [ParagraphStyle]
item = ParagraphStyle(),
start = "a".length,
end = "ab".length
)
)
)
val paragraph = MultiParagraph(
annotatedString = text,
style = TextStyle(textDirection = TextDirection.Rtl),
constraints = Constraints(),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
// the first character uses TextDirection.Content, text is Ltr
assertThat(paragraph.getParagraphDirection(0)).isEqualTo(ResolvedTextDirection.Ltr)
// the second character should use TextDirection.Rtl since it should inherit
// from main [ParagraphStyle]
assertThat(paragraph.getParagraphDirection(1)).isEqualTo(ResolvedTextDirection.Rtl)
}
@Test
fun maxIntrinsicWidth_withPlaceholder_inEm() {
val text = AnnotatedString(text = "ab")
val fontSize = 20
val width = 2.em
val placeholders = listOf(
Range(
Placeholder(width, 1.em, PlaceholderVerticalAlign.AboveBaseline),
0,
1
)
)
val paragraph = MultiParagraph(
annotatedString = text,
style = TextStyle(
fontSize = fontSize.sp,
fontFamily = fontFamilyMeasureFont
),
placeholders = placeholders,
constraints = Constraints(),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
// Rendered as below:
// | |b
// The placeholder takes the space of 2 characters
assertThat(paragraph.maxIntrinsicWidth).isEqualTo(fontSize * width.value + fontSize)
}
@Test
fun maxIntrinsicWidth_withPlaceholder_inSp() {
val text = AnnotatedString(text = "ab")
val fontSize = 20
val width = 30.sp
val placeholders = listOf(
Range(
Placeholder(width, 1.em, PlaceholderVerticalAlign.AboveBaseline),
0,
1
)
)
val paragraph = MultiParagraph(
annotatedString = text,
style = TextStyle(
fontSize = fontSize.sp,
fontFamily = fontFamilyMeasureFont
),
placeholders = placeholders,
constraints = Constraints(),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
// Rendered as below:
// | |b
// The placeholder takes 30 sp. In default density, it will be 30 pixels
assertThat(paragraph.maxIntrinsicWidth).isEqualTo(fontSize + width.value)
}
@Test
fun placeholderRects_withSingleParagraph() {
val text = AnnotatedString(text = "ab")
val fontSize = 20
val width = 2.em
val height = 1.em
val placeholders = listOf(
Range(
Placeholder(width, height, PlaceholderVerticalAlign.AboveBaseline),
0,
1
)
)
val paragraph = MultiParagraph(
annotatedString = text,
style = TextStyle(
fontSize = fontSize.sp,
fontFamily = fontFamilyMeasureFont
),
placeholders = placeholders,
constraints = Constraints(),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
assertThat(paragraph.placeholderRects).hasSize(1)
assertThat(paragraph.placeholderRects[0]).isEqualTo(
Rect(
left = 0f,
top = paragraph.firstBaseline - height.value * fontSize,
right = width.value * fontSize,
bottom = paragraph.firstBaseline
)
)
}
@Test
fun placeholderRects_withMultipleParagraphs() {
val text = createAnnotatedString("ab", "cd")
val fontSize = 20
val width = 2.em
val height = 1.em
val placeholders = listOf(
Range(
Placeholder(width, height, PlaceholderVerticalAlign.AboveBaseline),
0,
1
),
Range(
Placeholder(width, height, PlaceholderVerticalAlign.AboveBaseline),
2,
3
)
)
val paragraph = MultiParagraph(
annotatedString = text,
style = TextStyle(
fontSize = fontSize.sp,
fontFamily = fontFamilyMeasureFont
),
placeholders = placeholders,
constraints = Constraints(),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
assertThat(paragraph.placeholderRects).hasSize(2)
assertThat(paragraph.placeholderRects[0]).isEqualTo(
Rect(
left = 0f,
top = paragraph.firstBaseline - height.value * fontSize,
right = width.value * fontSize,
bottom = paragraph.firstBaseline
)
)
assertThat(paragraph.placeholderRects[1]).isEqualTo(
Rect(
left = 0f,
top = paragraph.lastBaseline - height.value * fontSize,
right = width.value * fontSize,
bottom = paragraph.lastBaseline
)
)
}
@Test(expected = IllegalArgumentException::class)
fun placeholderRects_overlapWithParagraph() {
val text = createAnnotatedString("ab", "cd")
val fontSize = 20
val width = 2.em
val height = 1.em
val placeholders = listOf(
Range(
Placeholder(width, height, PlaceholderVerticalAlign.AboveBaseline),
1,
3
)
)
MultiParagraph(
annotatedString = text,
style = TextStyle(
fontSize = fontSize.sp,
fontFamily = fontFamilyMeasureFont
),
placeholders = placeholders,
constraints = Constraints(),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
}
@Test(expected = IllegalArgumentException::class)
fun setMinWidthConstraints_notSupported() {
val minWidthConstraints = Constraints(minWidth = 100)
MultiParagraph(
annotatedString = AnnotatedString(""),
style = TextStyle(),
constraints = minWidthConstraints,
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
}
@Test(expected = IllegalArgumentException::class)
fun setMinHeightConstraints_notSupported() {
val minHeightConstraints = Constraints(minHeight = 100)
MultiParagraph(
annotatedString = AnnotatedString(""),
style = TextStyle(),
constraints = minHeightConstraints,
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
}
@Test
fun multiParagraphConstruction_doesNotThrow_ifNotAllParagraghsFitVertically() {
with(defaultDensity) {
val fontSize = 20.sp
val constraints = Constraints(
maxWidth = 10 * fontSize.roundToPx(),
maxHeight = fontSize.roundToPx() / 2
)
val text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
append("Lorem")
}
withStyle(ParagraphStyle()) {
append("Ipsum")
}
}
MultiParagraph(
annotatedString = text,
style = TextStyle(fontSize = fontSize, fontFamily = fontFamilyMeasureFont),
constraints = constraints,
density = this,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
}
}
@OptIn(ExperimentalTextApi::class)
@Test
fun multiParagraph_appliesBrush_toTheWholeText() = with(defaultDensity) {
val fontSize = 20.sp
val fontSizeInPx = fontSize.toPx()
val brush = Brush.verticalGradient(listOf(Color.Blue, Color.Red))
val multiParagraph = simpleMultiParagraph(
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Right)) {
append("Lorem")
}
withStyle(ParagraphStyle()) {
append("Ipsum")
}
},
style = TextStyle(
brush = brush,
fontSize = fontSize
),
width = fontSizeInPx * 5
).apply { disableAntialias() }
val multiParagraph2 = simpleMultiParagraph(
text = buildAnnotatedString {
append("Lorem\n")
append("Ipsum")
},
style = TextStyle(
brush = brush,
fontSize = fontSize
),
width = fontSizeInPx * 5
).apply { disableAntialias() }
assertThat(multiParagraph.bitmap(brush))
.isEqualToBitmap(multiParagraph2.bitmap(brush))
}
@OptIn(ExperimentalTextApi::class)
@Test
fun multiParagraph_overridesAlphaDuringDraw() = with(defaultDensity) {
val fontSize = 20.sp
val fontSizeInPx = fontSize.toPx()
val brush = Brush.verticalGradient(listOf(Color.Blue, Color.Red))
val multiParagraph = simpleMultiParagraph(
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Right)) {
append("Lorem")
}
withStyle(ParagraphStyle()) {
append("Ipsum")
}
},
style = TextStyle(
brush = brush,
alpha = 0.5f,
fontSize = fontSize
),
width = fontSizeInPx * 5
).apply { disableAntialias() }
val multiParagraph2 = simpleMultiParagraph(
text = buildAnnotatedString {
append("Lorem\n")
append("Ipsum")
},
style = TextStyle(
brush = brush,
fontSize = fontSize
),
width = fontSizeInPx * 5
).apply { disableAntialias() }
assertThat(multiParagraph.bitmap(brush))
.isEqualToBitmap(multiParagraph2.bitmap(brush, 0.5f))
}
private fun MultiParagraph.disableAntialias() {
paragraphInfoList.forEach {
(it.paragraph as AndroidParagraph).textPaint.isAntiAlias = false
}
}
/**
* Helper function which creates an AnnotatedString where each input string becomes a paragraph.
*/
private fun createAnnotatedString(vararg paragraphs: String) =
createAnnotatedString(paragraphs.toList())
/**
* Helper function which creates an AnnotatedString where each input string becomes a paragraph.
*/
private fun createAnnotatedString(paragraphs: List): AnnotatedString {
return buildAnnotatedString {
for (paragraph in paragraphs) {
pushStyle(ParagraphStyle())
append(paragraph)
pop()
}
}
}
private fun simpleMultiParagraphIntrinsics(
text: AnnotatedString,
fontSize: TextUnit = TextUnit.Unspecified,
placeholders: List> = listOf()
): MultiParagraphIntrinsics {
return MultiParagraphIntrinsics(
text,
style = TextStyle(
fontFamily = fontFamilyMeasureFont,
fontSize = fontSize
),
placeholders = placeholders,
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
}
private fun simpleMultiParagraph(
text: String,
style: TextStyle? = null,
fontSize: TextUnit = TextUnit.Unspecified,
maxLines: Int = Int.MAX_VALUE,
width: Float = Float.MAX_VALUE
): MultiParagraph {
return MultiParagraph(
annotatedString = createAnnotatedString(text),
style = TextStyle(
fontFamily = fontFamilyMeasureFont,
fontSize = fontSize
).merge(style),
maxLines = maxLines,
constraints = Constraints(maxWidth = width.ceilToInt()),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
}
private fun simpleMultiParagraph(
text: AnnotatedString,
style: TextStyle? = null,
fontSize: TextUnit = TextUnit.Unspecified,
maxLines: Int = Int.MAX_VALUE,
width: Float = Float.MAX_VALUE,
localeList: LocaleList? = null
): MultiParagraph {
return MultiParagraph(
annotatedString = text,
style = TextStyle(
fontFamily = fontFamilyMeasureFont,
fontSize = fontSize,
localeList = localeList
).merge(style),
maxLines = maxLines,
constraints = Constraints(maxWidth = width.ceilToInt()),
density = defaultDensity,
fontFamilyResolver = UncachedFontFamilyResolver(context)
)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy