[go: nahoru, domu]

blob: 8a80ae90991cf43944225ffdcd61d245ad827352 [file] [log] [blame]
Louis Pullen-Freilich26940682019-05-01 16:44:01 +01001package androidx.ui.engine.text.platform
Haoyu Zhang10fc6502018-11-09 15:50:11 -08002
Haoyu Zhang10fc6502018-11-09 15:50:11 -08003import android.graphics.Paint
Haoyu Zhang10fc6502018-11-09 15:50:11 -08004import android.text.TextPaint
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -08005import android.text.style.AbsoluteSizeSpan
Haoyu Zhang0c4fd022019-01-02 13:53:10 -08006import android.text.style.BackgroundColorSpan
Haoyu Zhang47f57a22018-12-12 13:16:40 -08007import android.text.style.ForegroundColorSpan
Haoyu Zhang7fc59172019-03-01 17:03:31 -08008import android.text.style.LeadingMarginSpan
Haoyu Zhang4b201d22018-12-18 14:27:38 -08009import android.text.style.LocaleSpan
Haoyu Zhang15750922019-02-19 13:44:17 -080010import android.text.style.ScaleXSpan
Haoyu Zhangb5677202018-12-19 14:22:18 -080011import android.text.style.StrikethroughSpan
12import android.text.style.UnderlineSpan
Haoyu Zhang97d5e722019-01-04 17:32:34 -080013import androidx.test.filters.SdkSuppress
Haoyu Zhang10fc6502018-11-09 15:50:11 -080014import androidx.test.filters.SmallTest
15import androidx.test.platform.app.InstrumentationRegistry
Haoyu Zhang2eb38cd2018-11-21 15:05:28 -080016import androidx.text.StaticLayoutCompat
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -080017import androidx.text.style.BaselineShiftSpan
Haoyu Zhang588aaef2019-02-20 14:47:39 -080018import androidx.text.style.FontFeatureSpan
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -080019import androidx.text.style.LetterSpacingSpan
Haoyu Zhang2a036c832019-03-08 11:57:13 -080020import androidx.text.style.ShadowSpan
Haoyu Zhang15750922019-02-19 13:44:17 -080021import androidx.text.style.SkewXSpan
Siyamed Sinir70f336a2018-12-27 10:37:09 -080022import androidx.text.style.TypefaceSpan
Haoyu Zhang97d5e722019-01-04 17:32:34 -080023import androidx.text.style.WordSpacingSpan
Haoyu Zhang7fc59172019-03-01 17:03:31 -080024import androidx.ui.core.px
Haoyu Zhang2a036c832019-03-08 11:57:13 -080025import androidx.ui.engine.geometry.Offset
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -080026import androidx.ui.engine.text.BaselineShift
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080027import androidx.ui.engine.text.FontStyle
Siyamed Sinir21796012018-12-26 13:51:17 -080028import androidx.ui.engine.text.FontSynthesis
Louis Pullen-Freilich26940682019-05-01 16:44:01 +010029import androidx.ui.engine.text.FontTestData.Companion.BASIC_MEASURE_FONT
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080030import androidx.ui.engine.text.FontWeight
Haoyu Zhang47f57a22018-12-12 13:16:40 -080031import androidx.ui.engine.text.ParagraphBuilder
Haoyu Zhang10fc6502018-11-09 15:50:11 -080032import androidx.ui.engine.text.ParagraphStyle
33import androidx.ui.engine.text.TextAlign
Haoyu Zhangb5677202018-12-19 14:22:18 -080034import androidx.ui.engine.text.TextDecoration
Louis Pullen-Freilich26940682019-05-01 16:44:01 +010035import androidx.ui.engine.text.TextGeometricTransform
36import androidx.ui.engine.text.TextIndent
Haoyu Zhang47f57a22018-12-12 13:16:40 -080037import androidx.ui.engine.text.TextStyle
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080038import androidx.ui.engine.text.font.FontFamily
39import androidx.ui.engine.text.font.asFontFamily
Haoyu Zhang4b201d22018-12-18 14:27:38 -080040import androidx.ui.engine.window.Locale
Siyamed Sinir00aa7172019-02-01 13:15:56 -080041import androidx.ui.matchers.equalToBitmap
42import androidx.ui.matchers.hasSpan
43import androidx.ui.matchers.hasSpanOnTop
Haoyu Zhang7fc59172019-03-01 17:03:31 -080044import androidx.ui.matchers.notHasSpan
George Mounta3145782019-05-02 10:08:03 -070045import androidx.ui.graphics.Color
Haoyu Zhang2a036c832019-03-08 11:57:13 -080046import androidx.ui.painting.Shadow
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080047import com.nhaarman.mockitokotlin2.any
48import com.nhaarman.mockitokotlin2.eq
49import com.nhaarman.mockitokotlin2.mock
50import com.nhaarman.mockitokotlin2.never
51import com.nhaarman.mockitokotlin2.spy
52import com.nhaarman.mockitokotlin2.times
53import com.nhaarman.mockitokotlin2.verify
Haoyu Zhangb5677202018-12-19 14:22:18 -080054import org.hamcrest.Matchers.equalTo
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080055import org.hamcrest.Matchers.not
56import org.hamcrest.Matchers.nullValue
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -080057import org.junit.Assert.assertFalse
Haoyu Zhang47f57a22018-12-12 13:16:40 -080058import org.junit.Assert.assertThat
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -080059import org.junit.Assert.assertTrue
Haoyu Zhang10fc6502018-11-09 15:50:11 -080060import org.junit.Before
61import org.junit.Test
62import org.junit.runner.RunWith
63import org.junit.runners.JUnit4
Haoyu Zhang10fc6502018-11-09 15:50:11 -080064import kotlin.math.ceil
65
66@RunWith(JUnit4::class)
67@SmallTest
68class ParagraphAndroidTest {
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080069 private lateinit var fontFamily: FontFamily
Haoyu Zhang10fc6502018-11-09 15:50:11 -080070
71 @Before
72 fun setup() {
Haoyu Zhang47f57a22018-12-12 13:16:40 -080073 // This sample font provides the following features:
74 // 1. The width of most of visible characters equals to font size.
75 // 2. The LTR/RTL characters are rendered as â–¶/â—€.
76 // 3. The fontMetrics passed to TextPaint has descend - ascend equal to 1.2 * fontSize.
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080077 fontFamily = BASIC_MEASURE_FONT.asFontFamily()
78 fontFamily.context = InstrumentationRegistry.getInstrumentation().context
Haoyu Zhang10fc6502018-11-09 15:50:11 -080079 }
80
81 @Test
82 fun draw_with_newline_and_line_break_default_values() {
George Mountb1cdd862018-12-21 10:54:42 -080083 val fontSize = 50.0f
Haoyu Zhang10fc6502018-11-09 15:50:11 -080084 for (text in arrayOf("abc\ndef", "\u05D0\u05D1\u05D2\n\u05D3\u05D4\u05D5")) {
85 val paragraphAndroid = simpleParagraph(
86 text = StringBuilder(text),
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080087 fontSize = fontSize,
88 fontFamily = fontFamily
Haoyu Zhang10fc6502018-11-09 15:50:11 -080089 )
90
91 // 2 chars width
92 paragraphAndroid.layout(width = 2 * fontSize)
93
94 val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
George Mountb1cdd862018-12-21 10:54:42 -080095 textPaint.textSize = fontSize
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080096 textPaint.typeface = TypefaceAdapter().create(fontFamily)
Haoyu Zhang10fc6502018-11-09 15:50:11 -080097
Haoyu Zhang2eb38cd2018-11-21 15:05:28 -080098 val staticLayout = StaticLayoutCompat.Builder(
99 text,
100 textPaint,
101 ceil(paragraphAndroid.width).toInt()
Haoyu Zhang10fc6502018-11-09 15:50:11 -0800102 )
Haoyu Zhang2eb38cd2018-11-21 15:05:28 -0800103 .setEllipsizedWidth(ceil(paragraphAndroid.width).toInt())
104 .build()
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800105 assertThat(paragraphAndroid.bitmap(), equalToBitmap(staticLayout.bitmap()))
Haoyu Zhang10fc6502018-11-09 15:50:11 -0800106 }
107 }
108
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800109 @Test
110 fun textStyle_setColorOnWholeText() {
111 val text = "abcde"
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800112 val textStyle = TextStyle(color = Color(0xFF0000FF.toInt()))
113
114 val paragraph = simpleParagraph(
115 text = text,
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800116 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800117 )
George Mountb1cdd862018-12-21 10:54:42 -0800118 paragraph.layout(100.0f)
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800119
120 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, text.length))
121 }
122
123 @Test
124 fun textStyle_setColorOnPartOfText() {
125 val text = "abcde"
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800126 val textStyle = TextStyle(color = Color(0xFF0000FF.toInt()))
127
128 val paragraph = simpleParagraph(
129 text = text,
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800130 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800131 )
George Mountb1cdd862018-12-21 10:54:42 -0800132 paragraph.layout(100.0f)
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800133
134 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, "abc".length))
135 }
136
137 @Test
138 fun textStyle_setColorTwice_lastOneOverwrite() {
139 val text = "abcde"
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800140 val textStyle = TextStyle(color = Color(0xFF0000FF.toInt()))
141 val textStyleOverwrite = TextStyle(color = Color(0xFF00FF00.toInt()))
142
143 val paragraph = simpleParagraph(
144 text = text,
145 textStyles = listOf(
146 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
147 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800148 )
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800149 )
George Mountb1cdd862018-12-21 10:54:42 -0800150 paragraph.layout(100.0f)
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800151
152 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, text.length))
153 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, "abc".length))
154 assertThat(
155 paragraph.underlyingText,
156 hasSpanOnTop(ForegroundColorSpan::class, 0, "abc".length)
157 )
158 }
159
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800160 @Test
Haoyu Zhangb5677202018-12-19 14:22:18 -0800161 fun testStyle_setTextDecorationOnWholeText_withLineThrough() {
162 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700163 val textStyle = TextStyle(decoration = TextDecoration.LineThrough)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800164
165 val paragraph = simpleParagraph(
166 text = text,
167 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
168 )
George Mountb1cdd862018-12-21 10:54:42 -0800169 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800170
171 assertThat(paragraph.underlyingText.toString(), equalTo(text))
172 assertThat(paragraph.underlyingText, hasSpan(StrikethroughSpan::class, 0, text.length))
173 }
174
175 @Test
176 fun testStyle_setTextDecorationOnWholeText_withUnderline() {
177 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700178 val textStyle = TextStyle(decoration = TextDecoration.Underline)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800179
180 val paragraph = simpleParagraph(
181 text = text,
182 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
183 )
George Mountb1cdd862018-12-21 10:54:42 -0800184 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800185
186 assertThat(paragraph.underlyingText.toString(), equalTo(text))
187 assertThat(paragraph.underlyingText, hasSpan(UnderlineSpan::class, 0, text.length))
188 }
189
190 @Test
191 fun testStyle_setTextDecorationOnPartText_withLineThrough() {
192 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700193 val textStyle = TextStyle(decoration = TextDecoration.LineThrough)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800194
195 val paragraph = simpleParagraph(
196 text = text,
197 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
198 )
George Mountb1cdd862018-12-21 10:54:42 -0800199 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800200
201 assertThat(paragraph.underlyingText.toString(), equalTo(text))
202 assertThat(paragraph.underlyingText, hasSpan(StrikethroughSpan::class, 0, "abc".length))
203 }
204
205 @Test
206 fun testStyle_setTextDecorationOnPartText_withUnderline() {
207 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700208 val textStyle = TextStyle(decoration = TextDecoration.Underline)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800209
210 val paragraph = simpleParagraph(
211 text = text,
212 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
213 )
George Mountb1cdd862018-12-21 10:54:42 -0800214 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800215
216 assertThat(paragraph.underlyingText.toString(), equalTo(text))
217 assertThat(paragraph.underlyingText, hasSpan(UnderlineSpan::class, 0, "abc".length))
218 }
219
220 @Test
221 fun testStyle_setTextDecoration_withLineThroughAndUnderline() {
222 val text = "abcde"
223 val textStyle = TextStyle(
224 decoration = TextDecoration.combine(
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700225 listOf(TextDecoration.LineThrough, TextDecoration.Underline)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800226 )
227 )
228
229 val paragraph = simpleParagraph(
230 text = text,
231 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
232 )
George Mountb1cdd862018-12-21 10:54:42 -0800233 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800234
235 assertThat(paragraph.underlyingText.toString(), equalTo(text))
236 assertThat(paragraph.underlyingText, hasSpan(UnderlineSpan::class, 0, "abc".length))
237 assertThat(paragraph.underlyingText, hasSpan(StrikethroughSpan::class, 0, "abc".length))
238 }
239
240 @Test
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800241 fun textStyle_setFontSizeOnWholeText() {
242 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800243 val fontSize = 20.0f
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800244 val paragraphWidth = text.length * fontSize
245 val textStyle = TextStyle(fontSize = fontSize)
246
247 val paragraph = simpleParagraph(
248 text = text,
249 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
250 )
251 paragraph.layout(paragraphWidth)
252
253 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, text.length))
254 }
255
256 @Test
257 fun textStyle_setFontSizeOnPartText() {
258 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800259 val fontSize = 20.0f
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800260 val paragraphWidth = text.length * fontSize
261 val textStyle = TextStyle(fontSize = fontSize)
262
263 val paragraph = simpleParagraph(
264 text = text,
265 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
266 )
267 paragraph.layout(paragraphWidth)
268
269 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, "abc".length))
270 }
271
272 @Test
273 fun textStyle_setFontSizeTwice_lastOneOverwrite() {
274 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800275 val fontSize = 20.0f
276 val fontSizeOverwrite = 30.0f
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800277 val paragraphWidth = text.length * fontSizeOverwrite
278 val textStyle = TextStyle(fontSize = fontSize)
279 val textStyleOverwrite = TextStyle(fontSize = fontSizeOverwrite)
280
281 val paragraph = simpleParagraph(
282 text = text,
283 textStyles = listOf(
284 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
285 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
286 )
287 )
288 paragraph.layout(paragraphWidth)
289
290 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, text.length))
291 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, "abc".length))
292 assertThat(
293 paragraph.underlyingText,
294 hasSpanOnTop(AbsoluteSizeSpan::class, 0, "abc".length)
295 )
296 }
297
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800298 @Test
299 fun textStyle_setLetterSpacingOnWholeText() {
300 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800301 val letterSpacing = 2.0f
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800302 val textStyle = TextStyle(letterSpacing = letterSpacing)
303
304 val paragraph = simpleParagraph(
305 text = text,
306 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
307 )
George Mountb1cdd862018-12-21 10:54:42 -0800308 paragraph.layout(100.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800309 assertThat(paragraph.underlyingText.toString(), equalTo(text))
310 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, text.length))
311 }
312
313 @Test
314 fun textStyle_setLetterSpacingOnPartText() {
315 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800316 val textStyle = TextStyle(letterSpacing = 2.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800317
318 val paragraph = simpleParagraph(
319 text = text,
320 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
321 )
George Mountb1cdd862018-12-21 10:54:42 -0800322 paragraph.layout(100.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800323 assertThat(paragraph.underlyingText.toString(), equalTo(text))
324 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, "abc".length))
325 }
326
327 @Test
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800328 fun textStyle_setLetterSpacingTwice_lastOneOverwrite() {
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800329 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800330 val textStyle = TextStyle(letterSpacing = 2.0f)
331 val textStyleOverwrite = TextStyle(letterSpacing = 3.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800332
333 val paragraph = simpleParagraph(
334 text = text,
335 textStyles = listOf(
336 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
337 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
338 )
339 )
George Mountb1cdd862018-12-21 10:54:42 -0800340 paragraph.layout(100.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800341 assertThat(paragraph.underlyingText.toString(), equalTo(text))
342 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, text.length))
343 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, "abc".length))
344 assertThat(
345 paragraph.underlyingText,
346 hasSpanOnTop(LetterSpacingSpan::class, 0, "abc".length)
347 )
348 }
349
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800350 @Test
Haoyu Zhang97d5e722019-01-04 17:32:34 -0800351 @SdkSuppress(minSdkVersion = 28)
352 fun textStyle_setWordSpacingOnWholeText() {
353 val text = "ab cd"
354 val wordSpacing = 2.0f
355 val textStyle = TextStyle(wordSpacing = wordSpacing)
356
357 val paragraph = simpleParagraph(
358 text = text,
359 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
360 )
361 // Notice that the width doesn't matter for this test.
362 paragraph.layout(100.0f)
363
364 assertThat(paragraph.underlyingText.toString(), equalTo(text))
365 assertThat(
366 paragraph.underlyingText,
367 hasSpan(WordSpacingSpan::class, 0, text.length) { span ->
368 span.wordSpacing == wordSpacing.toFloat()
369 }
370 )
371 }
372
373 @Test
374 @SdkSuppress(minSdkVersion = 28)
375 fun textStyle_setWordSpacingOnPartText() {
376 val text = "abc d"
377 val wordSpacing = 2.0f
378 val textStyle = TextStyle(wordSpacing = wordSpacing)
379
380 val paragraph = simpleParagraph(
381 text = text,
382 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
383 )
384 // Notice that the width doesn't matter for this test.
385 paragraph.layout(100.0f)
386
387 assertThat(paragraph.underlyingText.toString(), equalTo(text))
388 assertThat(
389 paragraph.underlyingText,
390 hasSpan(WordSpacingSpan::class, 0, "abc".length) { span ->
391 span.wordSpacing == wordSpacing.toFloat()
392 }
393 )
394 }
395
396 @Test
397 @SdkSuppress(minSdkVersion = 28)
398 fun textStyle_setWordSpacingTwice_lastOneOverwrite() {
399 val text = "abc d"
400 val wordSpacing = 2.0f
401 val textStyle = TextStyle(wordSpacing = wordSpacing)
402 val wordSpacingOverwrite = 3.0f
403 val textStyleOverwrite = TextStyle(wordSpacing = wordSpacingOverwrite)
404
405 val paragraph = simpleParagraph(
406 text = text,
407 textStyles = listOf(
408 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
409 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
410 )
411 )
412 // Notice that the width doesn't matter for this test.
413 paragraph.layout(100.0f)
414
415 assertThat(paragraph.underlyingText.toString(), equalTo(text))
416 assertThat(
417 paragraph.underlyingText,
418 hasSpan(WordSpacingSpan::class, 0, text.length) { span ->
419 span.wordSpacing == wordSpacing.toFloat()
420 }
421 )
422 assertThat(
423 paragraph.underlyingText,
424 hasSpan(WordSpacingSpan::class, 0, "abc".length) { span ->
425 span.wordSpacing == wordSpacingOverwrite.toFloat()
426 }
427 )
428 assertThat(
429 paragraph.underlyingText,
430 hasSpanOnTop(WordSpacingSpan::class, 0, "abc".length) { span ->
431 span.wordSpacing == wordSpacingOverwrite.toFloat()
432 }
433 )
434 }
435
436 @Test
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800437 fun textStyle_setBackgroundOnWholeText() {
438 val text = "abcde"
439 val color = Color(0xFF0000FF.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800440 val textStyle = TextStyle(background = color)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800441
442 val paragraph = simpleParagraph(
443 text = text,
444 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
445 )
George Mountb1cdd862018-12-21 10:54:42 -0800446 paragraph.layout(100.0f)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800447
448 assertThat(paragraph.underlyingText.toString(), equalTo(text))
449 assertThat(paragraph.underlyingText,
450 hasSpan(BackgroundColorSpan::class, 0, text.length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700451 span.backgroundColor == color.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800452 }
453 )
454 }
455
456 @Test
457 fun textStyle_setBackgroundOnPartText() {
458 val text = "abcde"
459 val color = Color(0xFF0000FF.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800460 val textStyle = TextStyle(background = color)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800461
462 val paragraph = simpleParagraph(
463 text = text,
464 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
465 )
George Mountb1cdd862018-12-21 10:54:42 -0800466 paragraph.layout(100.0f)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800467
468 assertThat(paragraph.underlyingText.toString(), equalTo(text))
469 assertThat(paragraph.underlyingText,
470 hasSpan(BackgroundColorSpan::class, 0, "abc".length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700471 span.backgroundColor == color.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800472 }
473 )
474 }
475
476 @Test
477 fun textStyle_setBackgroundTwice_lastOneOverwrite() {
478 val text = "abcde"
479 val color = Color(0xFF0000FF.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800480 val textStyle = TextStyle(background = color)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800481 val colorOverwrite = Color(0xFF00FF00.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800482 val textStyleOverwrite = TextStyle(background = colorOverwrite)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800483
484 val paragraph = simpleParagraph(
485 text = text,
486 textStyles = listOf(
487 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
488 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
489 )
490 )
George Mountb1cdd862018-12-21 10:54:42 -0800491 paragraph.layout(100.0f)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800492
493 assertThat(paragraph.underlyingText.toString(), equalTo(text))
494 assertThat(paragraph.underlyingText,
495 hasSpan(BackgroundColorSpan::class, 0, text.length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700496 span.backgroundColor == color.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800497 }
498 )
499 assertThat(paragraph.underlyingText,
500 hasSpan(BackgroundColorSpan::class, 0, "abc".length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700501 span.backgroundColor == colorOverwrite.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800502 }
503 )
504 assertThat(
505 paragraph.underlyingText,
506 hasSpanOnTop(BackgroundColorSpan::class, 0, "abc".length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700507 span.backgroundColor == colorOverwrite.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800508 }
509 )
510 }
511
512 @Test
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800513 fun textStyle_setLocaleOnWholeText() {
514 val text = "abcde"
515 val locale = Locale("en", "US")
516 val textStyle = TextStyle(locale = locale)
517
518 val paragraph = simpleParagraph(
519 text = text,
520 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
521 )
George Mountb1cdd862018-12-21 10:54:42 -0800522 paragraph.layout(100.0f)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800523
524 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, text.length))
525 }
526
527 @Test
528 fun textStyle_setLocaleOnPartText() {
529 val text = "abcde"
530 val locale = Locale("en", "US")
531 val textStyle = TextStyle(locale = locale)
532
533 val paragraph = simpleParagraph(
534 text = text,
535 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
536 )
George Mountb1cdd862018-12-21 10:54:42 -0800537 paragraph.layout(100.0f)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800538
539 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, "abc".length))
540 }
541
542 @Test
543 fun textStyle_setLocaleTwice_lastOneOverwrite() {
544 val text = "abcde"
545 val textStyle = TextStyle(locale = Locale("en", "US"))
546 val textStyleOverwrite = TextStyle(locale = Locale("ja", "JP"))
547
548 val paragraph = simpleParagraph(
549 text = text,
550 textStyles = listOf(
551 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
552 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
553 )
554 )
George Mountb1cdd862018-12-21 10:54:42 -0800555 paragraph.layout(100.0f)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800556
557 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, text.length))
558 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, "abc".length))
559 assertThat(
560 paragraph.underlyingText,
561 hasSpanOnTop(LocaleSpan::class, 0, "abc".length)
562 )
563 }
564
565 @Test
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -0800566 fun textStyle_setBaselineShiftOnWholeText() {
567 val text = "abcde"
568 val textStyle = TextStyle(baselineShift = BaselineShift.SUBSCRIPT)
569
570 val paragraph = simpleParagraph(
571 text = text,
572 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
573 )
574 // width is not important
575 paragraph.layout(100.0f)
576
577 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, text.length))
578 }
579
580 @Test
581 fun textStyle_setBaselineShiftOnPartText() {
582 val text = "abcde"
583 val textStyle = TextStyle(baselineShift = BaselineShift.SUPERSCRIPT)
584
585 val paragraph = simpleParagraph(
586 text = text,
587 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
588 )
589 // width is not important
590 paragraph.layout(100.0f)
591
592 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, "abc".length))
593 }
594
595 @Test
596 fun textStyle_setBaselineShiftTwice_LastOneOnTop() {
597 val text = "abcde"
598 val textStyle = TextStyle(baselineShift = BaselineShift.SUBSCRIPT)
599 val textStyleOverwrite = TextStyle(baselineShift = BaselineShift.SUPERSCRIPT)
600
601 val paragraph = simpleParagraph(
602 text = text,
603 textStyles = listOf(
604 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
605 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
606 )
607 )
608 // width is not important
609 paragraph.layout(100.0f)
610
611 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, text.length))
612 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, "abc".length))
613 assertThat(
614 paragraph.underlyingText,
615 hasSpanOnTop(BaselineShiftSpan::class, 0, "abc".length)
616 )
617 }
618
619 @Test
Haoyu Zhang15750922019-02-19 13:44:17 -0800620 fun textStyle_setTextGeometricTransformWithNull_noSpanSet() {
621 val text = "abcde"
622 val textStyle = TextStyle(textGeometricTransform = TextGeometricTransform(null, null))
623
624 val paragraph = simpleParagraph(
625 text = text,
626 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
627 )
628 // width is not important
629 paragraph.layout(100.0f)
630
631 assertThat(paragraph.underlyingText, not(hasSpan(ScaleXSpan::class, 0, text.length)))
632 assertThat(paragraph.underlyingText, not(hasSpan(SkewXSpan::class, 0, text.length)))
633 }
634
635 @Test
636 fun textStyle_setTextGeometricTransformWithScaleX() {
637 val text = "abcde"
638 val scaleX = 0.5f
639 val textStyle = TextStyle(textGeometricTransform = TextGeometricTransform(scaleX, null))
640
641 val paragraph = simpleParagraph(
642 text = text,
643 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
644 )
645 // width is not important
646 paragraph.layout(100.0f)
647
648 assertThat(
649 paragraph.underlyingText,
650 hasSpan(ScaleXSpan::class, 0, text.length) { it.scaleX == scaleX }
651 )
652 assertThat(paragraph.underlyingText, not(hasSpan(SkewXSpan::class, 0, text.length)))
653 }
654
655 @Test
656 fun textStyle_setTextGeometricTransformWithSkewX() {
657 val text = "aa"
658 val skewX = 1f
659 val textStyle = TextStyle(textGeometricTransform = TextGeometricTransform(null, skewX))
660
661 val paragraph = simpleParagraph(
662 text = text,
663 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
664 )
665 // width is not important
666 paragraph.layout(100.0f)
667
668 assertThat(
669 paragraph.underlyingText,
670 hasSpan(SkewXSpan::class, 0, text.length) { it.skewX == skewX }
671 )
672 assertThat(paragraph.underlyingText, not(hasSpan(ScaleXSpan::class, 0, text.length)))
673 }
674
675 @Test
Haoyu Zhang7fc59172019-03-01 17:03:31 -0800676 fun textStyle_setTextIndent_onWholeParagraph() {
677 val text = "abc\ndef"
678 val firstLine = 40
679 val restLine = 20
680 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
681
682 val paragraph = simpleParagraph(
683 text = text,
684 textStyles = listOf(
685 ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length)
686 )
687 )
688 // width is not important
689 paragraph.layout(100.0f)
690
691 assertThat(
692 paragraph.underlyingText,
693 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc".length) {
694 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
695 }
696 )
697 }
698
699 @Test
700 fun textStyle_setTextIndent_onPartParagraph() {
701 val text = "abc\ndef"
702 val firstLine = 40
703 val restLine = 20
704 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
705
706 val paragraph = simpleParagraph(
707 text = text,
708 textStyles = listOf(
709 ParagraphBuilder.TextStyleIndex(textStyle, 0, 1)
710 )
711 )
712 // width is not important
713 paragraph.layout(100.0f)
714
715 assertThat(
716 paragraph.underlyingText,
717 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc".length) {
718 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
719 }
720 )
721 }
722
723 @Test
724 fun textStyle_setTextIndent_lastCharIsLineFeed() {
725 val text = "abc\ndef"
726 val firstLine = 40
727 val restLine = 20
728 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
729
730 val paragraph = simpleParagraph(
731 text = text,
732 textStyles = listOf(
733 ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc\n".length)
734 )
735 )
736 // width is not important
737 paragraph.layout(100.0f)
738
739 assertThat(
740 paragraph.underlyingText,
741 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc".length) {
742 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
743 }
744 )
745 }
746
747 @Test
748 fun textStyle_setTextIndent_firstCharIsLineFeed() {
749 val text = "abc\ndef"
750 val firstLine = 40
751 val restLine = 20
752 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
753
754 val paragraph = simpleParagraph(
755 text = text,
756 textStyles = listOf(
757 ParagraphBuilder.TextStyleIndex(textStyle, "abc".length, "abc\nd".length)
758 )
759 )
760 // width is not important
761 paragraph.layout(100.0f)
762
763 assertThat(
764 paragraph.underlyingText,
765 hasSpan(LeadingMarginSpan.Standard::class, "abc\n".length, "abc\ndef".length) {
766 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
767 }
768 )
769 }
770
771 @Test
772 fun textStyle_setTextIndent_coverLineFeed() {
773 val text = "abc\ndef"
774 val firstLine = 40
775 val restLine = 20
776 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
777
778 val paragraph = simpleParagraph(
779 text = text,
780 textStyles = listOf(
781 ParagraphBuilder.TextStyleIndex(textStyle, "abc".length, "abc\n".length)
782 )
783 )
784 // width is not important
785 paragraph.layout(100.0f)
786
787 assertThat(
788 paragraph.underlyingText,
789 notHasSpan(LeadingMarginSpan.Standard::class, 0, text.length)
790 )
791 }
792
793 @Test
794 fun textStyle_setTextIndent_coverEmptyParagraph() {
795 val text = "abc\n\ndef"
796 val firstLine = 40
797 val restLine = 20
798 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
799
800 val paragraph = simpleParagraph(
801 text = text,
802 textStyles = listOf(
803 ParagraphBuilder.TextStyleIndex(textStyle, "abc".length, "abc\n\n".length)
804 )
805 )
806 // width is not important
807 paragraph.layout(100.0f)
808
809 assertThat(
810 paragraph.underlyingText,
811 hasSpan(LeadingMarginSpan.Standard::class, "abc\n".length, "abc\n\n".length)
812 )
813 }
814
815 @Test
816 fun textStyle_setTextIndent_coverMultiParagraph() {
817 val text = "abc\ndef\nghi"
818 val firstLine = 40
819 val restLine = 20
820 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
821
822 val paragraph = simpleParagraph(
823 text = text,
824 textStyles = listOf(
825 ParagraphBuilder.TextStyleIndex(textStyle, "ab".length, "abc\nd".length)
826 )
827 )
828 // width is not important
829 paragraph.layout(100.0f)
830
831 assertThat(
832 paragraph.underlyingText,
833 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc\ndef".length) {
834 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
835 }
836 )
837 }
838
839 @Test
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800840 fun textStyle_setShadow() {
841 val text = "abcde"
842 val color = Color(0xFF00FF00.toInt())
843 val offset = Offset(1f, 2f)
844 val radius = 3.px
845 val textStyle = TextStyle(shadow = Shadow(color, offset, radius))
846
847 val paragraph = simpleParagraph(
848 text = text,
849 textStyles = listOf(
850 ParagraphBuilder.TextStyleIndex(textStyle, start = 0, end = text.length)
851 )
852 )
853 // width is not important
854 paragraph.layout(100.0f)
855
856 assertThat(
857 paragraph.underlyingText,
858 hasSpan(ShadowSpan::class, start = 0, end = text.length) {
George Mounta3145782019-05-02 10:08:03 -0700859 return@hasSpan it.color == color.toArgb() &&
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800860 it.offsetX == offset.dx &&
861 it.offsetY == offset.dy &&
862 it.radius == radius.value
863 }
864 )
865 }
866
867 @Test
868 fun textStyle_setShadowTwice_lastOnTop() {
869 val text = "abcde"
870 val color = Color(0xFF00FF00.toInt())
871 val offset = Offset(1f, 2f)
872 val radius = 3.px
873 val textStyle = TextStyle(shadow = Shadow(color, offset, radius))
874
875 val colorOverwrite = Color(0xFF0000FF.toInt())
876 val offsetOverwrite = Offset(3f, 2f)
877 val radiusOverwrite = 1.px
878 val textStyleOverwrite = TextStyle(
879 shadow = Shadow(colorOverwrite, offsetOverwrite, radiusOverwrite)
880 )
881
882 val paragraph = simpleParagraph(
883 text = text,
884 textStyles = listOf(
885 ParagraphBuilder.TextStyleIndex(textStyle, start = 0, end = text.length),
886 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, start = 0, end = "abc".length)
887 )
888 )
889 // width is not important
890 paragraph.layout(100.0f)
891
892 assertThat(
893 paragraph.underlyingText,
894 hasSpan(ShadowSpan::class, start = 0, end = text.length) {
George Mounta3145782019-05-02 10:08:03 -0700895 return@hasSpan it.color == color.toArgb() &&
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800896 it.offsetX == offset.dx &&
897 it.offsetY == offset.dy &&
898 it.radius == radius.value
899 }
900 )
901 assertThat(
902 paragraph.underlyingText,
903 hasSpanOnTop(ShadowSpan::class, start = 0, end = "abc".length) {
George Mounta3145782019-05-02 10:08:03 -0700904 return@hasSpanOnTop it.color == colorOverwrite.toArgb() &&
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800905 it.offsetX == offsetOverwrite.dx &&
906 it.offsetY == offsetOverwrite.dy &&
907 it.radius == radiusOverwrite.value
908 }
909 )
910 }
911
912 @Test
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800913 fun textStyle_fontFamily_addsTypefaceSpanWithCorrectTypeface() {
914 val text = "abcde"
915 val textStyle = TextStyle(
916 fontFamily = fontFamily,
917 fontStyle = FontStyle.italic,
918 fontWeight = FontWeight.bold
919 )
920 val expectedTypeface = TypefaceAdapter().create(
921 fontFamily = fontFamily,
922 fontStyle = FontStyle.italic,
923 fontWeight = FontWeight.bold
924 )
Siyamed Sinir37298412018-12-27 19:47:15 -0800925 val expectedStart = 0
926 val expectedEnd = "abc".length
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800927
928 val paragraph = simpleParagraph(
929 text = text,
Siyamed Sinir37298412018-12-27 19:47:15 -0800930 textStyles = listOf(
931 ParagraphBuilder.TextStyleIndex(
932 textStyle,
933 expectedStart,
934 expectedEnd
935 )
936 )
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800937 )
George Mountb1cdd862018-12-21 10:54:42 -0800938 paragraph.layout(100.0f)
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800939
940 assertThat(paragraph.underlyingText.toString(), equalTo(text))
941 assertThat(
942 paragraph.underlyingText,
Siyamed Sinir37298412018-12-27 19:47:15 -0800943 hasSpan(TypefaceSpan::class, expectedStart, expectedEnd) { span ->
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800944 span.typeface == expectedTypeface
Siyamed Sinir37298412018-12-27 19:47:15 -0800945 })
946 }
947
948 @Test
949 fun textStyle_fontFamily_whenFontSynthesizeTurnedOff() {
950 val text = "abcde"
951 val textStyle = TextStyle(
952 fontFamily = fontFamily,
953 fontStyle = FontStyle.italic,
954 fontWeight = FontWeight.bold,
955 fontSynthesis = FontSynthesis.none
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800956 )
Siyamed Sinir37298412018-12-27 19:47:15 -0800957 val expectedTypeface = TypefaceAdapter().create(
958 fontFamily = fontFamily,
959 fontStyle = FontStyle.italic,
960 fontWeight = FontWeight.bold,
961 fontSynthesis = FontSynthesis.none
962 )
963 val expectedStart = 0
964 val expectedEnd = "abc".length
965
966 val paragraph = simpleParagraph(
967 text = text,
968 textStyles = listOf(
969 ParagraphBuilder.TextStyleIndex(
970 textStyle,
971 expectedStart,
972 expectedEnd
973 )
974 )
975 )
George Mountb1cdd862018-12-21 10:54:42 -0800976 paragraph.layout(100.0f)
Siyamed Sinir37298412018-12-27 19:47:15 -0800977
978 assertThat(paragraph.underlyingText.toString(), equalTo(text))
979 assertThat(
980 paragraph.underlyingText,
981 hasSpan(TypefaceSpan::class, expectedStart, expectedEnd) { span ->
982 span.typeface == expectedTypeface
983 })
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800984 }
985
986 @Test
Haoyu Zhang588aaef2019-02-20 14:47:39 -0800987 fun textStyle_fontFeatureSetting_setSpanOnText() {
988 val text = "abc"
989 val fontFeatureSettings = "\"kern\" 0"
990 val textStyle = TextStyle(fontFeatureSettings = fontFeatureSettings)
991
992 val paragraph = simpleParagraph(
993 text = text,
994 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
995 )
996 // width is not important
997 paragraph.layout(100.0f)
998
999 assertThat(
1000 paragraph.underlyingText,
1001 hasSpan(FontFeatureSpan::class, 0, "abc".length) {
1002 it.fontFeatureSettings == fontFeatureSettings
1003 })
1004 }
1005
1006 @Test
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001007 fun testEmptyFontFamily() {
1008 val typefaceAdapter = mock<TypefaceAdapter>()
1009 val paragraph = simpleParagraph(
1010 text = "abc",
1011 typefaceAdapter = typefaceAdapter
1012 )
George Mountb1cdd862018-12-21 10:54:42 -08001013 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001014
1015 verify(typefaceAdapter, never()).create(
1016 fontFamily = any(),
1017 fontWeight = any(),
Siyamed Sinir21796012018-12-26 13:51:17 -08001018 fontStyle = any(),
1019 fontSynthesis = any()
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001020 )
1021 assertThat(paragraph.textPaint.typeface, nullValue())
1022 }
1023
1024 @Test
1025 fun testEmptyFontFamily_withBoldFontWeightSelection() {
1026 val typefaceAdapter = spy(TypefaceAdapter())
1027
1028 val paragraph = simpleParagraph(
1029 text = "abc",
1030 fontFamily = null,
1031 fontWeight = FontWeight.bold,
1032 typefaceAdapter = typefaceAdapter
1033 )
George Mountb1cdd862018-12-21 10:54:42 -08001034 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001035
1036 verify(typefaceAdapter, times(1)).create(
1037 fontFamily = eq(null),
1038 fontWeight = eq(FontWeight.bold),
Siyamed Sinir21796012018-12-26 13:51:17 -08001039 fontStyle = eq(FontStyle.normal),
1040 fontSynthesis = eq(FontSynthesis.all)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001041 )
1042
1043 val typeface = paragraph.textPaint.typeface
1044 assertThat(typeface, not(nullValue()))
1045 assertThat(typeface.isBold, equalTo(true))
1046 assertThat(typeface.isItalic, equalTo(false))
1047 }
1048
1049 @Test
1050 fun testEmptyFontFamily_withFontStyleSelection() {
1051 val typefaceAdapter = spy(TypefaceAdapter())
1052 val paragraph = simpleParagraph(
1053 text = "abc",
1054 fontFamily = null,
1055 fontStyle = FontStyle.italic,
1056 typefaceAdapter = typefaceAdapter
1057 )
George Mountb1cdd862018-12-21 10:54:42 -08001058 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001059
1060 verify(typefaceAdapter, times(1)).create(
1061 fontFamily = eq(null),
1062 fontWeight = eq(FontWeight.normal),
Siyamed Sinir21796012018-12-26 13:51:17 -08001063 fontStyle = eq(FontStyle.italic),
1064 fontSynthesis = eq(FontSynthesis.all)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001065 )
1066
1067 val typeface = paragraph.textPaint.typeface
1068 assertThat(typeface, not(nullValue()))
1069 assertThat(typeface.isBold, equalTo(false))
1070 assertThat(typeface.isItalic, equalTo(true))
1071 }
1072
1073 @Test
1074 fun testFontFamily_withGenericFamilyName() {
1075 val typefaceAdapter = spy(TypefaceAdapter())
1076 val fontFamily = FontFamily("sans-serif")
1077
1078 val paragraph = simpleParagraph(
1079 text = "abc",
1080 fontFamily = fontFamily,
1081 typefaceAdapter = typefaceAdapter
1082 )
George Mountb1cdd862018-12-21 10:54:42 -08001083 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001084
1085 verify(typefaceAdapter, times(1)).create(
1086 fontFamily = eq(fontFamily),
1087 fontWeight = eq(FontWeight.normal),
Siyamed Sinir21796012018-12-26 13:51:17 -08001088 fontStyle = eq(FontStyle.normal),
1089 fontSynthesis = eq(FontSynthesis.all)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001090 )
1091
1092 val typeface = paragraph.textPaint.typeface
1093 assertThat(typeface, not(nullValue()))
1094 assertThat(typeface.isBold, equalTo(false))
1095 assertThat(typeface.isItalic, equalTo(false))
1096 }
1097
1098 @Test
1099 fun testFontFamily_withCustomFont() {
1100 val typefaceAdapter = spy(TypefaceAdapter())
1101 val paragraph = simpleParagraph(
1102 text = "abc",
1103 fontFamily = fontFamily,
1104 typefaceAdapter = typefaceAdapter
1105 )
George Mountb1cdd862018-12-21 10:54:42 -08001106 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001107
1108 verify(typefaceAdapter, times(1)).create(
1109 fontFamily = eq(fontFamily),
1110 fontWeight = eq(FontWeight.normal),
Siyamed Sinir21796012018-12-26 13:51:17 -08001111 fontStyle = eq(FontStyle.normal),
1112 fontSynthesis = eq(FontSynthesis.all)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001113 )
1114 val typeface = paragraph.textPaint.typeface
1115 assertThat(typeface.isBold, equalTo(false))
1116 assertThat(typeface.isItalic, equalTo(false))
1117 }
1118
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -08001119 @Test
1120 fun testEllipsis_withMaxLineEqualsNull_doesNotEllipsis() {
1121 val text = "abc"
1122 val fontSize = 20f
1123 val paragraphWidth = (text.length - 1) * fontSize
1124 val paragraph = simpleParagraph(
1125 text = text,
1126 fontFamily = fontFamily,
1127 fontSize = fontSize,
1128 ellipsis = true
1129 )
1130 paragraph.layout(paragraphWidth)
1131 for (i in 0 until paragraph.lineCount) {
1132 assertFalse(paragraph.isEllipsisApplied(i))
1133 }
1134 }
1135
1136 @Test
1137 fun testEllipsis_withMaxLinesLessThanTextLines_doesEllipsis() {
1138 val text = "abcde"
1139 val fontSize = 100f
1140 // Note that on API 21, if the next line only contains 1 character, ellipsis won't work
1141 val paragraphWidth = (text.length - 1.5f) * fontSize
1142 val paragraph = simpleParagraph(
1143 text = text,
1144 fontFamily = fontFamily,
1145 fontSize = fontSize,
1146 ellipsis = true,
1147 maxLines = 1
1148 )
1149 paragraph.layout(paragraphWidth)
1150
1151 assertTrue(paragraph.isEllipsisApplied(0))
1152 }
1153
1154 @Test
1155 fun testEllipsis_withMaxLinesMoreThanTextLines_doesNotEllipsis() {
1156 val text = "abc"
1157 val fontSize = 100f
1158 val paragraphWidth = (text.length - 1) * fontSize
1159 val maxLines = ceil(text.length * fontSize / paragraphWidth).toInt()
1160 val paragraph = simpleParagraph(
1161 text = text,
1162 fontFamily = fontFamily,
1163 fontSize = fontSize,
1164 ellipsis = true,
1165 maxLines = maxLines
1166 )
1167 paragraph.layout(paragraphWidth)
1168
1169 for (i in 0 until paragraph.lineCount) {
1170 assertFalse(paragraph.isEllipsisApplied(i))
1171 }
1172 }
1173
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001174 private fun simpleParagraph(
1175 text: CharSequence = "",
Haoyu Zhang47f57a22018-12-12 13:16:40 -08001176 textStyles: List<ParagraphBuilder.TextStyleIndex> = listOf(),
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001177 textAlign: TextAlign? = null,
George Mountb1cdd862018-12-21 10:54:42 -08001178 fontSize: Float? = null,
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -08001179 ellipsis: Boolean? = null,
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001180 maxLines: Int? = null,
1181 fontFamily: FontFamily? = null,
1182 fontWeight: FontWeight? = null,
1183 fontStyle: FontStyle? = null,
1184 typefaceAdapter: TypefaceAdapter = TypefaceAdapter()
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001185 ): ParagraphAndroid {
1186 return ParagraphAndroid(
1187 text = StringBuilder(text),
Haoyu Zhang47f57a22018-12-12 13:16:40 -08001188 textStyles = textStyles,
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001189 typefaceAdapter = typefaceAdapter,
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001190 paragraphStyle = ParagraphStyle(
1191 textAlign = textAlign,
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -08001192 ellipsis = ellipsis,
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001193 maxLines = maxLines,
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001194 fontFamily = fontFamily,
1195 fontSize = fontSize,
1196 fontWeight = fontWeight,
1197 fontStyle = fontStyle
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001198 )
1199 )
1200 }
George Mountb1cdd862018-12-21 10:54:42 -08001201}