[go: nahoru, domu]

blob: eee732ac4e4f08e6938c7509f7798f97db37502e [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 Zhang24eb32f2019-05-21 16:16:37 -070010import android.text.style.RelativeSizeSpan
Haoyu Zhang15750922019-02-19 13:44:17 -080011import android.text.style.ScaleXSpan
Haoyu Zhangb5677202018-12-19 14:22:18 -080012import android.text.style.StrikethroughSpan
13import android.text.style.UnderlineSpan
Haoyu Zhang97d5e722019-01-04 17:32:34 -080014import androidx.test.filters.SdkSuppress
Haoyu Zhang10fc6502018-11-09 15:50:11 -080015import androidx.test.filters.SmallTest
16import androidx.test.platform.app.InstrumentationRegistry
Haoyu Zhang2eb38cd2018-11-21 15:05:28 -080017import androidx.text.StaticLayoutCompat
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -080018import androidx.text.style.BaselineShiftSpan
Haoyu Zhang588aaef2019-02-20 14:47:39 -080019import androidx.text.style.FontFeatureSpan
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -080020import androidx.text.style.LetterSpacingSpan
Haoyu Zhang2a036c832019-03-08 11:57:13 -080021import androidx.text.style.ShadowSpan
Haoyu Zhang15750922019-02-19 13:44:17 -080022import androidx.text.style.SkewXSpan
Siyamed Sinir70f336a2018-12-27 10:37:09 -080023import androidx.text.style.TypefaceSpan
Haoyu Zhang97d5e722019-01-04 17:32:34 -080024import androidx.text.style.WordSpacingSpan
Haoyu Zhang7fc59172019-03-01 17:03:31 -080025import androidx.ui.core.px
Haoyu Zhang2a036c832019-03-08 11:57:13 -080026import androidx.ui.engine.geometry.Offset
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -080027import androidx.ui.engine.text.BaselineShift
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080028import androidx.ui.engine.text.FontStyle
Siyamed Sinir21796012018-12-26 13:51:17 -080029import androidx.ui.engine.text.FontSynthesis
Louis Pullen-Freilich26940682019-05-01 16:44:01 +010030import androidx.ui.engine.text.FontTestData.Companion.BASIC_MEASURE_FONT
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080031import androidx.ui.engine.text.FontWeight
Haoyu Zhang47f57a22018-12-12 13:16:40 -080032import androidx.ui.engine.text.ParagraphBuilder
Haoyu Zhang10fc6502018-11-09 15:50:11 -080033import androidx.ui.engine.text.ParagraphStyle
34import androidx.ui.engine.text.TextAlign
Haoyu Zhangb5677202018-12-19 14:22:18 -080035import androidx.ui.engine.text.TextDecoration
Louis Pullen-Freilich26940682019-05-01 16:44:01 +010036import androidx.ui.engine.text.TextGeometricTransform
37import androidx.ui.engine.text.TextIndent
Haoyu Zhang47f57a22018-12-12 13:16:40 -080038import androidx.ui.engine.text.TextStyle
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080039import androidx.ui.engine.text.font.FontFamily
40import androidx.ui.engine.text.font.asFontFamily
Haoyu Zhang4b201d22018-12-18 14:27:38 -080041import androidx.ui.engine.window.Locale
Siyamed Sinir00aa7172019-02-01 13:15:56 -080042import androidx.ui.matchers.equalToBitmap
43import androidx.ui.matchers.hasSpan
44import androidx.ui.matchers.hasSpanOnTop
Haoyu Zhang7fc59172019-03-01 17:03:31 -080045import androidx.ui.matchers.notHasSpan
George Mounta3145782019-05-02 10:08:03 -070046import androidx.ui.graphics.Color
Haoyu Zhang2a036c832019-03-08 11:57:13 -080047import androidx.ui.painting.Shadow
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080048import com.nhaarman.mockitokotlin2.any
49import com.nhaarman.mockitokotlin2.eq
50import com.nhaarman.mockitokotlin2.mock
51import com.nhaarman.mockitokotlin2.never
52import com.nhaarman.mockitokotlin2.spy
53import com.nhaarman.mockitokotlin2.times
54import com.nhaarman.mockitokotlin2.verify
Haoyu Zhangb5677202018-12-19 14:22:18 -080055import org.hamcrest.Matchers.equalTo
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080056import org.hamcrest.Matchers.not
57import org.hamcrest.Matchers.nullValue
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -080058import org.junit.Assert.assertFalse
Haoyu Zhang47f57a22018-12-12 13:16:40 -080059import org.junit.Assert.assertThat
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -080060import org.junit.Assert.assertTrue
Haoyu Zhang10fc6502018-11-09 15:50:11 -080061import org.junit.Before
62import org.junit.Test
63import org.junit.runner.RunWith
64import org.junit.runners.JUnit4
Haoyu Zhang10fc6502018-11-09 15:50:11 -080065import kotlin.math.ceil
66
67@RunWith(JUnit4::class)
68@SmallTest
69class ParagraphAndroidTest {
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080070 private lateinit var fontFamily: FontFamily
Haoyu Zhang10fc6502018-11-09 15:50:11 -080071
72 @Before
73 fun setup() {
Haoyu Zhang47f57a22018-12-12 13:16:40 -080074 // This sample font provides the following features:
75 // 1. The width of most of visible characters equals to font size.
76 // 2. The LTR/RTL characters are rendered as â–¶/â—€.
77 // 3. The fontMetrics passed to TextPaint has descend - ascend equal to 1.2 * fontSize.
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080078 fontFamily = BASIC_MEASURE_FONT.asFontFamily()
79 fontFamily.context = InstrumentationRegistry.getInstrumentation().context
Haoyu Zhang10fc6502018-11-09 15:50:11 -080080 }
81
82 @Test
83 fun draw_with_newline_and_line_break_default_values() {
George Mountb1cdd862018-12-21 10:54:42 -080084 val fontSize = 50.0f
Haoyu Zhang10fc6502018-11-09 15:50:11 -080085 for (text in arrayOf("abc\ndef", "\u05D0\u05D1\u05D2\n\u05D3\u05D4\u05D5")) {
86 val paragraphAndroid = simpleParagraph(
87 text = StringBuilder(text),
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080088 fontSize = fontSize,
89 fontFamily = fontFamily
Haoyu Zhang10fc6502018-11-09 15:50:11 -080090 )
91
92 // 2 chars width
93 paragraphAndroid.layout(width = 2 * fontSize)
94
95 val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
George Mountb1cdd862018-12-21 10:54:42 -080096 textPaint.textSize = fontSize
Siyamed Sinirf18dcca2018-12-20 18:25:10 -080097 textPaint.typeface = TypefaceAdapter().create(fontFamily)
Haoyu Zhang10fc6502018-11-09 15:50:11 -080098
Haoyu Zhang2eb38cd2018-11-21 15:05:28 -080099 val staticLayout = StaticLayoutCompat.Builder(
100 text,
101 textPaint,
102 ceil(paragraphAndroid.width).toInt()
Haoyu Zhang10fc6502018-11-09 15:50:11 -0800103 )
Haoyu Zhang2eb38cd2018-11-21 15:05:28 -0800104 .setEllipsizedWidth(ceil(paragraphAndroid.width).toInt())
105 .build()
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800106 assertThat(paragraphAndroid.bitmap(), equalToBitmap(staticLayout.bitmap()))
Haoyu Zhang10fc6502018-11-09 15:50:11 -0800107 }
108 }
109
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800110 @Test
111 fun textStyle_setColorOnWholeText() {
112 val text = "abcde"
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800113 val textStyle = TextStyle(color = Color(0xFF0000FF.toInt()))
114
115 val paragraph = simpleParagraph(
116 text = text,
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800117 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800118 )
George Mountb1cdd862018-12-21 10:54:42 -0800119 paragraph.layout(100.0f)
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800120
121 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, text.length))
122 }
123
124 @Test
125 fun textStyle_setColorOnPartOfText() {
126 val text = "abcde"
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800127 val textStyle = TextStyle(color = Color(0xFF0000FF.toInt()))
128
129 val paragraph = simpleParagraph(
130 text = text,
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800131 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800132 )
George Mountb1cdd862018-12-21 10:54:42 -0800133 paragraph.layout(100.0f)
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800134
135 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, "abc".length))
136 }
137
138 @Test
139 fun textStyle_setColorTwice_lastOneOverwrite() {
140 val text = "abcde"
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800141 val textStyle = TextStyle(color = Color(0xFF0000FF.toInt()))
142 val textStyleOverwrite = TextStyle(color = Color(0xFF00FF00.toInt()))
143
144 val paragraph = simpleParagraph(
145 text = text,
146 textStyles = listOf(
147 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
148 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800149 )
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800150 )
George Mountb1cdd862018-12-21 10:54:42 -0800151 paragraph.layout(100.0f)
Haoyu Zhang47f57a22018-12-12 13:16:40 -0800152
153 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, text.length))
154 assertThat(paragraph.underlyingText, hasSpan(ForegroundColorSpan::class, 0, "abc".length))
155 assertThat(
156 paragraph.underlyingText,
157 hasSpanOnTop(ForegroundColorSpan::class, 0, "abc".length)
158 )
159 }
160
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800161 @Test
Haoyu Zhangb5677202018-12-19 14:22:18 -0800162 fun testStyle_setTextDecorationOnWholeText_withLineThrough() {
163 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700164 val textStyle = TextStyle(decoration = TextDecoration.LineThrough)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800165
166 val paragraph = simpleParagraph(
167 text = text,
168 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
169 )
George Mountb1cdd862018-12-21 10:54:42 -0800170 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800171
172 assertThat(paragraph.underlyingText.toString(), equalTo(text))
173 assertThat(paragraph.underlyingText, hasSpan(StrikethroughSpan::class, 0, text.length))
174 }
175
176 @Test
177 fun testStyle_setTextDecorationOnWholeText_withUnderline() {
178 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700179 val textStyle = TextStyle(decoration = TextDecoration.Underline)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800180
181 val paragraph = simpleParagraph(
182 text = text,
183 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
184 )
George Mountb1cdd862018-12-21 10:54:42 -0800185 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800186
187 assertThat(paragraph.underlyingText.toString(), equalTo(text))
188 assertThat(paragraph.underlyingText, hasSpan(UnderlineSpan::class, 0, text.length))
189 }
190
191 @Test
192 fun testStyle_setTextDecorationOnPartText_withLineThrough() {
193 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700194 val textStyle = TextStyle(decoration = TextDecoration.LineThrough)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800195
196 val paragraph = simpleParagraph(
197 text = text,
198 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
199 )
George Mountb1cdd862018-12-21 10:54:42 -0800200 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800201
202 assertThat(paragraph.underlyingText.toString(), equalTo(text))
203 assertThat(paragraph.underlyingText, hasSpan(StrikethroughSpan::class, 0, "abc".length))
204 }
205
206 @Test
207 fun testStyle_setTextDecorationOnPartText_withUnderline() {
208 val text = "abcde"
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700209 val textStyle = TextStyle(decoration = TextDecoration.Underline)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800210
211 val paragraph = simpleParagraph(
212 text = text,
213 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
214 )
George Mountb1cdd862018-12-21 10:54:42 -0800215 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800216
217 assertThat(paragraph.underlyingText.toString(), equalTo(text))
218 assertThat(paragraph.underlyingText, hasSpan(UnderlineSpan::class, 0, "abc".length))
219 }
220
221 @Test
222 fun testStyle_setTextDecoration_withLineThroughAndUnderline() {
223 val text = "abcde"
224 val textStyle = TextStyle(
225 decoration = TextDecoration.combine(
Siyamed Sinir9fd89f42019-05-20 18:04:29 -0700226 listOf(TextDecoration.LineThrough, TextDecoration.Underline)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800227 )
228 )
229
230 val paragraph = simpleParagraph(
231 text = text,
232 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
233 )
George Mountb1cdd862018-12-21 10:54:42 -0800234 paragraph.layout(100.0f)
Haoyu Zhangb5677202018-12-19 14:22:18 -0800235
236 assertThat(paragraph.underlyingText.toString(), equalTo(text))
237 assertThat(paragraph.underlyingText, hasSpan(UnderlineSpan::class, 0, "abc".length))
238 assertThat(paragraph.underlyingText, hasSpan(StrikethroughSpan::class, 0, "abc".length))
239 }
240
241 @Test
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800242 fun textStyle_setFontSizeOnWholeText() {
243 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800244 val fontSize = 20.0f
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800245 val paragraphWidth = text.length * fontSize
246 val textStyle = TextStyle(fontSize = fontSize)
247
248 val paragraph = simpleParagraph(
249 text = text,
250 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
251 )
252 paragraph.layout(paragraphWidth)
253
254 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, text.length))
255 }
256
257 @Test
258 fun textStyle_setFontSizeOnPartText() {
259 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800260 val fontSize = 20.0f
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800261 val paragraphWidth = text.length * fontSize
262 val textStyle = TextStyle(fontSize = fontSize)
263
264 val paragraph = simpleParagraph(
265 text = text,
266 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
267 )
268 paragraph.layout(paragraphWidth)
269
270 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, "abc".length))
271 }
272
273 @Test
274 fun textStyle_setFontSizeTwice_lastOneOverwrite() {
275 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800276 val fontSize = 20.0f
277 val fontSizeOverwrite = 30.0f
Haoyu Zhangbb51ccf2018-12-17 15:07:43 -0800278 val paragraphWidth = text.length * fontSizeOverwrite
279 val textStyle = TextStyle(fontSize = fontSize)
280 val textStyleOverwrite = TextStyle(fontSize = fontSizeOverwrite)
281
282 val paragraph = simpleParagraph(
283 text = text,
284 textStyles = listOf(
285 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
286 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
287 )
288 )
289 paragraph.layout(paragraphWidth)
290
291 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, text.length))
292 assertThat(paragraph.underlyingText, hasSpan(AbsoluteSizeSpan::class, 0, "abc".length))
293 assertThat(
294 paragraph.underlyingText,
295 hasSpanOnTop(AbsoluteSizeSpan::class, 0, "abc".length)
296 )
297 }
298
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800299 @Test
Haoyu Zhang24eb32f2019-05-21 16:16:37 -0700300 fun textStyle_setFontSizeScaleOnWholeText() {
301 val text = "abcde"
302 val fontSizeScale = 2.0f
303 val textStyle = TextStyle(fontSizeScale = fontSizeScale)
304
305 val paragraph = simpleParagraph(
306 text = text,
307 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
308 )
309 paragraph.layout(100f)
310
311 assertThat(
312 paragraph.underlyingText,
313 hasSpan(RelativeSizeSpan::class, 0, text.length) {
314 it.sizeChange == fontSizeScale
315 }
316 )
317 }
318
319 @Test
320 fun textStyle_setFontSizeScaleOnPartText() {
321 val text = "abcde"
322 val fontSizeScale = 2.0f
323 val textStyle = TextStyle(fontSizeScale = fontSizeScale)
324
325 val paragraph = simpleParagraph(
326 text = text,
327 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
328 )
329 paragraph.layout(100f)
330
331 assertThat(
332 paragraph.underlyingText,
333 hasSpan(RelativeSizeSpan::class, 0, "abc".length) {
334 it.sizeChange == fontSizeScale
335 }
336 )
337 }
338
339 @Test
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800340 fun textStyle_setLetterSpacingOnWholeText() {
341 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800342 val letterSpacing = 2.0f
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800343 val textStyle = TextStyle(letterSpacing = letterSpacing)
344
345 val paragraph = simpleParagraph(
346 text = text,
347 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
348 )
George Mountb1cdd862018-12-21 10:54:42 -0800349 paragraph.layout(100.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800350 assertThat(paragraph.underlyingText.toString(), equalTo(text))
351 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, text.length))
352 }
353
354 @Test
355 fun textStyle_setLetterSpacingOnPartText() {
356 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800357 val textStyle = TextStyle(letterSpacing = 2.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800358
359 val paragraph = simpleParagraph(
360 text = text,
361 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
362 )
George Mountb1cdd862018-12-21 10:54:42 -0800363 paragraph.layout(100.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800364 assertThat(paragraph.underlyingText.toString(), equalTo(text))
365 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, "abc".length))
366 }
367
368 @Test
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800369 fun textStyle_setLetterSpacingTwice_lastOneOverwrite() {
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800370 val text = "abcde"
George Mountb1cdd862018-12-21 10:54:42 -0800371 val textStyle = TextStyle(letterSpacing = 2.0f)
372 val textStyleOverwrite = TextStyle(letterSpacing = 3.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800373
374 val paragraph = simpleParagraph(
375 text = text,
376 textStyles = listOf(
377 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
378 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
379 )
380 )
George Mountb1cdd862018-12-21 10:54:42 -0800381 paragraph.layout(100.0f)
Haoyu Zhang1588ba0f2018-12-19 16:47:10 -0800382 assertThat(paragraph.underlyingText.toString(), equalTo(text))
383 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, text.length))
384 assertThat(paragraph.underlyingText, hasSpan(LetterSpacingSpan::class, 0, "abc".length))
385 assertThat(
386 paragraph.underlyingText,
387 hasSpanOnTop(LetterSpacingSpan::class, 0, "abc".length)
388 )
389 }
390
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800391 @Test
haoyuf6e7c0c2019-06-18 16:04:02 -0700392 @SdkSuppress(minSdkVersion = 29)
Haoyu Zhang97d5e722019-01-04 17:32:34 -0800393 fun textStyle_setWordSpacingOnWholeText() {
394 val text = "ab cd"
395 val wordSpacing = 2.0f
396 val textStyle = TextStyle(wordSpacing = wordSpacing)
397
398 val paragraph = simpleParagraph(
399 text = text,
400 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
401 )
402 // Notice that the width doesn't matter for this test.
403 paragraph.layout(100.0f)
404
405 assertThat(paragraph.underlyingText.toString(), equalTo(text))
406 assertThat(
407 paragraph.underlyingText,
408 hasSpan(WordSpacingSpan::class, 0, text.length) { span ->
409 span.wordSpacing == wordSpacing.toFloat()
410 }
411 )
412 }
413
414 @Test
haoyuf6e7c0c2019-06-18 16:04:02 -0700415 @SdkSuppress(minSdkVersion = 29)
Haoyu Zhang97d5e722019-01-04 17:32:34 -0800416 fun textStyle_setWordSpacingOnPartText() {
417 val text = "abc d"
418 val wordSpacing = 2.0f
419 val textStyle = TextStyle(wordSpacing = wordSpacing)
420
421 val paragraph = simpleParagraph(
422 text = text,
423 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
424 )
425 // Notice that the width doesn't matter for this test.
426 paragraph.layout(100.0f)
427
428 assertThat(paragraph.underlyingText.toString(), equalTo(text))
429 assertThat(
430 paragraph.underlyingText,
431 hasSpan(WordSpacingSpan::class, 0, "abc".length) { span ->
432 span.wordSpacing == wordSpacing.toFloat()
433 }
434 )
435 }
436
437 @Test
haoyuf6e7c0c2019-06-18 16:04:02 -0700438 @SdkSuppress(minSdkVersion = 29)
Haoyu Zhang97d5e722019-01-04 17:32:34 -0800439 fun textStyle_setWordSpacingTwice_lastOneOverwrite() {
440 val text = "abc d"
441 val wordSpacing = 2.0f
442 val textStyle = TextStyle(wordSpacing = wordSpacing)
443 val wordSpacingOverwrite = 3.0f
444 val textStyleOverwrite = TextStyle(wordSpacing = wordSpacingOverwrite)
445
446 val paragraph = simpleParagraph(
447 text = text,
448 textStyles = listOf(
449 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
450 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
451 )
452 )
453 // Notice that the width doesn't matter for this test.
454 paragraph.layout(100.0f)
455
456 assertThat(paragraph.underlyingText.toString(), equalTo(text))
457 assertThat(
458 paragraph.underlyingText,
459 hasSpan(WordSpacingSpan::class, 0, text.length) { span ->
460 span.wordSpacing == wordSpacing.toFloat()
461 }
462 )
463 assertThat(
464 paragraph.underlyingText,
465 hasSpan(WordSpacingSpan::class, 0, "abc".length) { span ->
466 span.wordSpacing == wordSpacingOverwrite.toFloat()
467 }
468 )
469 assertThat(
470 paragraph.underlyingText,
471 hasSpanOnTop(WordSpacingSpan::class, 0, "abc".length) { span ->
472 span.wordSpacing == wordSpacingOverwrite.toFloat()
473 }
474 )
475 }
476
477 @Test
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800478 fun textStyle_setBackgroundOnWholeText() {
479 val text = "abcde"
480 val color = Color(0xFF0000FF.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800481 val textStyle = TextStyle(background = color)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800482
483 val paragraph = simpleParagraph(
484 text = text,
485 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
486 )
George Mountb1cdd862018-12-21 10:54:42 -0800487 paragraph.layout(100.0f)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800488
489 assertThat(paragraph.underlyingText.toString(), equalTo(text))
490 assertThat(paragraph.underlyingText,
491 hasSpan(BackgroundColorSpan::class, 0, text.length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700492 span.backgroundColor == color.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800493 }
494 )
495 }
496
497 @Test
498 fun textStyle_setBackgroundOnPartText() {
499 val text = "abcde"
500 val color = Color(0xFF0000FF.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800501 val textStyle = TextStyle(background = color)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800502
503 val paragraph = simpleParagraph(
504 text = text,
505 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
506 )
George Mountb1cdd862018-12-21 10:54:42 -0800507 paragraph.layout(100.0f)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800508
509 assertThat(paragraph.underlyingText.toString(), equalTo(text))
510 assertThat(paragraph.underlyingText,
511 hasSpan(BackgroundColorSpan::class, 0, "abc".length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700512 span.backgroundColor == color.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800513 }
514 )
515 }
516
517 @Test
518 fun textStyle_setBackgroundTwice_lastOneOverwrite() {
519 val text = "abcde"
520 val color = Color(0xFF0000FF.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800521 val textStyle = TextStyle(background = color)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800522 val colorOverwrite = Color(0xFF00FF00.toInt())
Haoyu Zhang437d9452019-01-04 17:34:04 -0800523 val textStyleOverwrite = TextStyle(background = colorOverwrite)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800524
525 val paragraph = simpleParagraph(
526 text = text,
527 textStyles = listOf(
528 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
529 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
530 )
531 )
George Mountb1cdd862018-12-21 10:54:42 -0800532 paragraph.layout(100.0f)
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800533
534 assertThat(paragraph.underlyingText.toString(), equalTo(text))
535 assertThat(paragraph.underlyingText,
536 hasSpan(BackgroundColorSpan::class, 0, text.length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700537 span.backgroundColor == color.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800538 }
539 )
540 assertThat(paragraph.underlyingText,
541 hasSpan(BackgroundColorSpan::class, 0, "abc".length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700542 span.backgroundColor == colorOverwrite.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800543 }
544 )
545 assertThat(
546 paragraph.underlyingText,
547 hasSpanOnTop(BackgroundColorSpan::class, 0, "abc".length) { span ->
George Mounta3145782019-05-02 10:08:03 -0700548 span.backgroundColor == colorOverwrite.toArgb()
Haoyu Zhang0c4fd022019-01-02 13:53:10 -0800549 }
550 )
551 }
552
553 @Test
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800554 fun textStyle_setLocaleOnWholeText() {
555 val text = "abcde"
556 val locale = Locale("en", "US")
557 val textStyle = TextStyle(locale = locale)
558
559 val paragraph = simpleParagraph(
560 text = text,
561 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
562 )
George Mountb1cdd862018-12-21 10:54:42 -0800563 paragraph.layout(100.0f)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800564
565 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, text.length))
566 }
567
568 @Test
569 fun textStyle_setLocaleOnPartText() {
570 val text = "abcde"
571 val locale = Locale("en", "US")
572 val textStyle = TextStyle(locale = locale)
573
574 val paragraph = simpleParagraph(
575 text = text,
576 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
577 )
George Mountb1cdd862018-12-21 10:54:42 -0800578 paragraph.layout(100.0f)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800579
580 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, "abc".length))
581 }
582
583 @Test
584 fun textStyle_setLocaleTwice_lastOneOverwrite() {
585 val text = "abcde"
586 val textStyle = TextStyle(locale = Locale("en", "US"))
587 val textStyleOverwrite = TextStyle(locale = Locale("ja", "JP"))
588
589 val paragraph = simpleParagraph(
590 text = text,
591 textStyles = listOf(
592 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
593 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
594 )
595 )
George Mountb1cdd862018-12-21 10:54:42 -0800596 paragraph.layout(100.0f)
Haoyu Zhang4b201d22018-12-18 14:27:38 -0800597
598 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, text.length))
599 assertThat(paragraph.underlyingText, hasSpan(LocaleSpan::class, 0, "abc".length))
600 assertThat(
601 paragraph.underlyingText,
602 hasSpanOnTop(LocaleSpan::class, 0, "abc".length)
603 )
604 }
605
606 @Test
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -0800607 fun textStyle_setBaselineShiftOnWholeText() {
608 val text = "abcde"
Siyamed Sinirf544c152019-05-21 12:12:21 -0700609 val textStyle = TextStyle(baselineShift = BaselineShift.Subscript)
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -0800610
611 val paragraph = simpleParagraph(
612 text = text,
613 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
614 )
615 // width is not important
616 paragraph.layout(100.0f)
617
618 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, text.length))
619 }
620
621 @Test
622 fun textStyle_setBaselineShiftOnPartText() {
623 val text = "abcde"
Siyamed Sinirf544c152019-05-21 12:12:21 -0700624 val textStyle = TextStyle(baselineShift = BaselineShift.Superscript)
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -0800625
626 val paragraph = simpleParagraph(
627 text = text,
628 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
629 )
630 // width is not important
631 paragraph.layout(100.0f)
632
633 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, "abc".length))
634 }
635
636 @Test
637 fun textStyle_setBaselineShiftTwice_LastOneOnTop() {
638 val text = "abcde"
Siyamed Sinirf544c152019-05-21 12:12:21 -0700639 val textStyle = TextStyle(baselineShift = BaselineShift.Subscript)
640 val textStyleOverwrite = TextStyle(baselineShift = BaselineShift.Superscript)
Haoyu Zhang40ce6bf2019-01-28 15:58:20 -0800641
642 val paragraph = simpleParagraph(
643 text = text,
644 textStyles = listOf(
645 ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length),
646 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, 0, "abc".length)
647 )
648 )
649 // width is not important
650 paragraph.layout(100.0f)
651
652 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, text.length))
653 assertThat(paragraph.underlyingText, hasSpan(BaselineShiftSpan::class, 0, "abc".length))
654 assertThat(
655 paragraph.underlyingText,
656 hasSpanOnTop(BaselineShiftSpan::class, 0, "abc".length)
657 )
658 }
659
660 @Test
Haoyu Zhang15750922019-02-19 13:44:17 -0800661 fun textStyle_setTextGeometricTransformWithNull_noSpanSet() {
662 val text = "abcde"
663 val textStyle = TextStyle(textGeometricTransform = TextGeometricTransform(null, null))
664
665 val paragraph = simpleParagraph(
666 text = text,
667 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
668 )
669 // width is not important
670 paragraph.layout(100.0f)
671
672 assertThat(paragraph.underlyingText, not(hasSpan(ScaleXSpan::class, 0, text.length)))
673 assertThat(paragraph.underlyingText, not(hasSpan(SkewXSpan::class, 0, text.length)))
674 }
675
676 @Test
677 fun textStyle_setTextGeometricTransformWithScaleX() {
678 val text = "abcde"
679 val scaleX = 0.5f
680 val textStyle = TextStyle(textGeometricTransform = TextGeometricTransform(scaleX, null))
681
682 val paragraph = simpleParagraph(
683 text = text,
684 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
685 )
686 // width is not important
687 paragraph.layout(100.0f)
688
689 assertThat(
690 paragraph.underlyingText,
691 hasSpan(ScaleXSpan::class, 0, text.length) { it.scaleX == scaleX }
692 )
693 assertThat(paragraph.underlyingText, not(hasSpan(SkewXSpan::class, 0, text.length)))
694 }
695
696 @Test
697 fun textStyle_setTextGeometricTransformWithSkewX() {
698 val text = "aa"
699 val skewX = 1f
700 val textStyle = TextStyle(textGeometricTransform = TextGeometricTransform(null, skewX))
701
702 val paragraph = simpleParagraph(
703 text = text,
704 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, text.length))
705 )
706 // width is not important
707 paragraph.layout(100.0f)
708
709 assertThat(
710 paragraph.underlyingText,
711 hasSpan(SkewXSpan::class, 0, text.length) { it.skewX == skewX }
712 )
713 assertThat(paragraph.underlyingText, not(hasSpan(ScaleXSpan::class, 0, text.length)))
714 }
715
716 @Test
Haoyu Zhang7fc59172019-03-01 17:03:31 -0800717 fun textStyle_setTextIndent_onWholeParagraph() {
718 val text = "abc\ndef"
719 val firstLine = 40
720 val restLine = 20
721 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
722
723 val paragraph = simpleParagraph(
724 text = text,
725 textStyles = listOf(
726 ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length)
727 )
728 )
729 // width is not important
730 paragraph.layout(100.0f)
731
732 assertThat(
733 paragraph.underlyingText,
734 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc".length) {
735 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
736 }
737 )
738 }
739
740 @Test
741 fun textStyle_setTextIndent_onPartParagraph() {
742 val text = "abc\ndef"
743 val firstLine = 40
744 val restLine = 20
745 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
746
747 val paragraph = simpleParagraph(
748 text = text,
749 textStyles = listOf(
750 ParagraphBuilder.TextStyleIndex(textStyle, 0, 1)
751 )
752 )
753 // width is not important
754 paragraph.layout(100.0f)
755
756 assertThat(
757 paragraph.underlyingText,
758 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc".length) {
759 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
760 }
761 )
762 }
763
764 @Test
765 fun textStyle_setTextIndent_lastCharIsLineFeed() {
766 val text = "abc\ndef"
767 val firstLine = 40
768 val restLine = 20
769 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
770
771 val paragraph = simpleParagraph(
772 text = text,
773 textStyles = listOf(
774 ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc\n".length)
775 )
776 )
777 // width is not important
778 paragraph.layout(100.0f)
779
780 assertThat(
781 paragraph.underlyingText,
782 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc".length) {
783 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
784 }
785 )
786 }
787
788 @Test
789 fun textStyle_setTextIndent_firstCharIsLineFeed() {
790 val text = "abc\ndef"
791 val firstLine = 40
792 val restLine = 20
793 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
794
795 val paragraph = simpleParagraph(
796 text = text,
797 textStyles = listOf(
798 ParagraphBuilder.TextStyleIndex(textStyle, "abc".length, "abc\nd".length)
799 )
800 )
801 // width is not important
802 paragraph.layout(100.0f)
803
804 assertThat(
805 paragraph.underlyingText,
806 hasSpan(LeadingMarginSpan.Standard::class, "abc\n".length, "abc\ndef".length) {
807 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
808 }
809 )
810 }
811
812 @Test
813 fun textStyle_setTextIndent_coverLineFeed() {
814 val text = "abc\ndef"
815 val firstLine = 40
816 val restLine = 20
817 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
818
819 val paragraph = simpleParagraph(
820 text = text,
821 textStyles = listOf(
822 ParagraphBuilder.TextStyleIndex(textStyle, "abc".length, "abc\n".length)
823 )
824 )
825 // width is not important
826 paragraph.layout(100.0f)
827
828 assertThat(
829 paragraph.underlyingText,
830 notHasSpan(LeadingMarginSpan.Standard::class, 0, text.length)
831 )
832 }
833
834 @Test
835 fun textStyle_setTextIndent_coverEmptyParagraph() {
836 val text = "abc\n\ndef"
837 val firstLine = 40
838 val restLine = 20
839 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
840
841 val paragraph = simpleParagraph(
842 text = text,
843 textStyles = listOf(
844 ParagraphBuilder.TextStyleIndex(textStyle, "abc".length, "abc\n\n".length)
845 )
846 )
847 // width is not important
848 paragraph.layout(100.0f)
849
850 assertThat(
851 paragraph.underlyingText,
852 hasSpan(LeadingMarginSpan.Standard::class, "abc\n".length, "abc\n\n".length)
853 )
854 }
855
856 @Test
857 fun textStyle_setTextIndent_coverMultiParagraph() {
858 val text = "abc\ndef\nghi"
859 val firstLine = 40
860 val restLine = 20
861 val textStyle = TextStyle(textIndent = TextIndent(firstLine.px, restLine.px))
862
863 val paragraph = simpleParagraph(
864 text = text,
865 textStyles = listOf(
866 ParagraphBuilder.TextStyleIndex(textStyle, "ab".length, "abc\nd".length)
867 )
868 )
869 // width is not important
870 paragraph.layout(100.0f)
871
872 assertThat(
873 paragraph.underlyingText,
874 hasSpan(LeadingMarginSpan.Standard::class, 0, "abc\ndef".length) {
875 it.getLeadingMargin(true) == firstLine && it.getLeadingMargin(false) == restLine
876 }
877 )
878 }
879
880 @Test
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800881 fun textStyle_setShadow() {
882 val text = "abcde"
883 val color = Color(0xFF00FF00.toInt())
884 val offset = Offset(1f, 2f)
885 val radius = 3.px
886 val textStyle = TextStyle(shadow = Shadow(color, offset, radius))
887
888 val paragraph = simpleParagraph(
889 text = text,
890 textStyles = listOf(
891 ParagraphBuilder.TextStyleIndex(textStyle, start = 0, end = text.length)
892 )
893 )
894 // width is not important
895 paragraph.layout(100.0f)
896
897 assertThat(
898 paragraph.underlyingText,
899 hasSpan(ShadowSpan::class, start = 0, end = text.length) {
George Mounta3145782019-05-02 10:08:03 -0700900 return@hasSpan it.color == color.toArgb() &&
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800901 it.offsetX == offset.dx &&
902 it.offsetY == offset.dy &&
903 it.radius == radius.value
904 }
905 )
906 }
907
908 @Test
909 fun textStyle_setShadowTwice_lastOnTop() {
910 val text = "abcde"
911 val color = Color(0xFF00FF00.toInt())
912 val offset = Offset(1f, 2f)
913 val radius = 3.px
914 val textStyle = TextStyle(shadow = Shadow(color, offset, radius))
915
916 val colorOverwrite = Color(0xFF0000FF.toInt())
917 val offsetOverwrite = Offset(3f, 2f)
918 val radiusOverwrite = 1.px
919 val textStyleOverwrite = TextStyle(
920 shadow = Shadow(colorOverwrite, offsetOverwrite, radiusOverwrite)
921 )
922
923 val paragraph = simpleParagraph(
924 text = text,
925 textStyles = listOf(
926 ParagraphBuilder.TextStyleIndex(textStyle, start = 0, end = text.length),
927 ParagraphBuilder.TextStyleIndex(textStyleOverwrite, start = 0, end = "abc".length)
928 )
929 )
930 // width is not important
931 paragraph.layout(100.0f)
932
933 assertThat(
934 paragraph.underlyingText,
935 hasSpan(ShadowSpan::class, start = 0, end = text.length) {
George Mounta3145782019-05-02 10:08:03 -0700936 return@hasSpan it.color == color.toArgb() &&
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800937 it.offsetX == offset.dx &&
938 it.offsetY == offset.dy &&
939 it.radius == radius.value
940 }
941 )
942 assertThat(
943 paragraph.underlyingText,
944 hasSpanOnTop(ShadowSpan::class, start = 0, end = "abc".length) {
George Mounta3145782019-05-02 10:08:03 -0700945 return@hasSpanOnTop it.color == colorOverwrite.toArgb() &&
Haoyu Zhang2a036c832019-03-08 11:57:13 -0800946 it.offsetX == offsetOverwrite.dx &&
947 it.offsetY == offsetOverwrite.dy &&
948 it.radius == radiusOverwrite.value
949 }
950 )
951 }
952
953 @Test
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800954 fun textStyle_fontFamily_addsTypefaceSpanWithCorrectTypeface() {
955 val text = "abcde"
956 val textStyle = TextStyle(
957 fontFamily = fontFamily,
Siyamed Siniradf982d2019-05-20 17:35:13 -0700958 fontStyle = FontStyle.Italic,
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800959 fontWeight = FontWeight.bold
960 )
961 val expectedTypeface = TypefaceAdapter().create(
962 fontFamily = fontFamily,
Siyamed Siniradf982d2019-05-20 17:35:13 -0700963 fontStyle = FontStyle.Italic,
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800964 fontWeight = FontWeight.bold
965 )
Siyamed Sinir37298412018-12-27 19:47:15 -0800966 val expectedStart = 0
967 val expectedEnd = "abc".length
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800968
969 val paragraph = simpleParagraph(
970 text = text,
Siyamed Sinir37298412018-12-27 19:47:15 -0800971 textStyles = listOf(
972 ParagraphBuilder.TextStyleIndex(
973 textStyle,
974 expectedStart,
975 expectedEnd
976 )
977 )
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800978 )
George Mountb1cdd862018-12-21 10:54:42 -0800979 paragraph.layout(100.0f)
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800980
981 assertThat(paragraph.underlyingText.toString(), equalTo(text))
982 assertThat(
983 paragraph.underlyingText,
Siyamed Sinir37298412018-12-27 19:47:15 -0800984 hasSpan(TypefaceSpan::class, expectedStart, expectedEnd) { span ->
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800985 span.typeface == expectedTypeface
Siyamed Sinir37298412018-12-27 19:47:15 -0800986 })
987 }
988
989 @Test
990 fun textStyle_fontFamily_whenFontSynthesizeTurnedOff() {
991 val text = "abcde"
992 val textStyle = TextStyle(
993 fontFamily = fontFamily,
Siyamed Siniradf982d2019-05-20 17:35:13 -0700994 fontStyle = FontStyle.Italic,
Siyamed Sinir37298412018-12-27 19:47:15 -0800995 fontWeight = FontWeight.bold,
Siyamed Sinir934a3442019-06-18 12:57:13 -0700996 fontSynthesis = FontSynthesis.None
Siyamed Sinir70f336a2018-12-27 10:37:09 -0800997 )
Siyamed Sinir37298412018-12-27 19:47:15 -0800998 val expectedTypeface = TypefaceAdapter().create(
999 fontFamily = fontFamily,
Siyamed Siniradf982d2019-05-20 17:35:13 -07001000 fontStyle = FontStyle.Italic,
Siyamed Sinir37298412018-12-27 19:47:15 -08001001 fontWeight = FontWeight.bold,
Siyamed Sinir934a3442019-06-18 12:57:13 -07001002 fontSynthesis = FontSynthesis.None
Siyamed Sinir37298412018-12-27 19:47:15 -08001003 )
1004 val expectedStart = 0
1005 val expectedEnd = "abc".length
1006
1007 val paragraph = simpleParagraph(
1008 text = text,
1009 textStyles = listOf(
1010 ParagraphBuilder.TextStyleIndex(
1011 textStyle,
1012 expectedStart,
1013 expectedEnd
1014 )
1015 )
1016 )
George Mountb1cdd862018-12-21 10:54:42 -08001017 paragraph.layout(100.0f)
Siyamed Sinir37298412018-12-27 19:47:15 -08001018
1019 assertThat(paragraph.underlyingText.toString(), equalTo(text))
1020 assertThat(
1021 paragraph.underlyingText,
1022 hasSpan(TypefaceSpan::class, expectedStart, expectedEnd) { span ->
1023 span.typeface == expectedTypeface
1024 })
Siyamed Sinir70f336a2018-12-27 10:37:09 -08001025 }
1026
1027 @Test
Haoyu Zhang588aaef2019-02-20 14:47:39 -08001028 fun textStyle_fontFeatureSetting_setSpanOnText() {
1029 val text = "abc"
1030 val fontFeatureSettings = "\"kern\" 0"
1031 val textStyle = TextStyle(fontFeatureSettings = fontFeatureSettings)
1032
1033 val paragraph = simpleParagraph(
1034 text = text,
1035 textStyles = listOf(ParagraphBuilder.TextStyleIndex(textStyle, 0, "abc".length))
1036 )
1037 // width is not important
1038 paragraph.layout(100.0f)
1039
1040 assertThat(
1041 paragraph.underlyingText,
1042 hasSpan(FontFeatureSpan::class, 0, "abc".length) {
1043 it.fontFeatureSettings == fontFeatureSettings
1044 })
1045 }
1046
1047 @Test
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001048 fun testEmptyFontFamily() {
1049 val typefaceAdapter = mock<TypefaceAdapter>()
1050 val paragraph = simpleParagraph(
1051 text = "abc",
1052 typefaceAdapter = typefaceAdapter
1053 )
George Mountb1cdd862018-12-21 10:54:42 -08001054 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001055
1056 verify(typefaceAdapter, never()).create(
1057 fontFamily = any(),
1058 fontWeight = any(),
Siyamed Sinir21796012018-12-26 13:51:17 -08001059 fontStyle = any(),
1060 fontSynthesis = any()
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001061 )
1062 assertThat(paragraph.textPaint.typeface, nullValue())
1063 }
1064
1065 @Test
1066 fun testEmptyFontFamily_withBoldFontWeightSelection() {
1067 val typefaceAdapter = spy(TypefaceAdapter())
1068
1069 val paragraph = simpleParagraph(
1070 text = "abc",
1071 fontFamily = null,
1072 fontWeight = FontWeight.bold,
1073 typefaceAdapter = typefaceAdapter
1074 )
George Mountb1cdd862018-12-21 10:54:42 -08001075 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001076
1077 verify(typefaceAdapter, times(1)).create(
1078 fontFamily = eq(null),
1079 fontWeight = eq(FontWeight.bold),
Siyamed Siniradf982d2019-05-20 17:35:13 -07001080 fontStyle = eq(FontStyle.Normal),
Siyamed Sinir934a3442019-06-18 12:57:13 -07001081 fontSynthesis = eq(FontSynthesis.All)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001082 )
1083
1084 val typeface = paragraph.textPaint.typeface
1085 assertThat(typeface, not(nullValue()))
1086 assertThat(typeface.isBold, equalTo(true))
1087 assertThat(typeface.isItalic, equalTo(false))
1088 }
1089
1090 @Test
1091 fun testEmptyFontFamily_withFontStyleSelection() {
1092 val typefaceAdapter = spy(TypefaceAdapter())
1093 val paragraph = simpleParagraph(
1094 text = "abc",
1095 fontFamily = null,
Siyamed Siniradf982d2019-05-20 17:35:13 -07001096 fontStyle = FontStyle.Italic,
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001097 typefaceAdapter = typefaceAdapter
1098 )
George Mountb1cdd862018-12-21 10:54:42 -08001099 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001100
1101 verify(typefaceAdapter, times(1)).create(
1102 fontFamily = eq(null),
1103 fontWeight = eq(FontWeight.normal),
Siyamed Siniradf982d2019-05-20 17:35:13 -07001104 fontStyle = eq(FontStyle.Italic),
Siyamed Sinir934a3442019-06-18 12:57:13 -07001105 fontSynthesis = eq(FontSynthesis.All)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001106 )
1107
1108 val typeface = paragraph.textPaint.typeface
1109 assertThat(typeface, not(nullValue()))
1110 assertThat(typeface.isBold, equalTo(false))
1111 assertThat(typeface.isItalic, equalTo(true))
1112 }
1113
1114 @Test
1115 fun testFontFamily_withGenericFamilyName() {
1116 val typefaceAdapter = spy(TypefaceAdapter())
1117 val fontFamily = FontFamily("sans-serif")
1118
1119 val paragraph = simpleParagraph(
1120 text = "abc",
1121 fontFamily = fontFamily,
1122 typefaceAdapter = typefaceAdapter
1123 )
George Mountb1cdd862018-12-21 10:54:42 -08001124 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001125
1126 verify(typefaceAdapter, times(1)).create(
1127 fontFamily = eq(fontFamily),
1128 fontWeight = eq(FontWeight.normal),
Siyamed Siniradf982d2019-05-20 17:35:13 -07001129 fontStyle = eq(FontStyle.Normal),
Siyamed Sinir934a3442019-06-18 12:57:13 -07001130 fontSynthesis = eq(FontSynthesis.All)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001131 )
1132
1133 val typeface = paragraph.textPaint.typeface
1134 assertThat(typeface, not(nullValue()))
1135 assertThat(typeface.isBold, equalTo(false))
1136 assertThat(typeface.isItalic, equalTo(false))
1137 }
1138
1139 @Test
1140 fun testFontFamily_withCustomFont() {
1141 val typefaceAdapter = spy(TypefaceAdapter())
1142 val paragraph = simpleParagraph(
1143 text = "abc",
1144 fontFamily = fontFamily,
1145 typefaceAdapter = typefaceAdapter
1146 )
George Mountb1cdd862018-12-21 10:54:42 -08001147 paragraph.layout(Float.MAX_VALUE)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001148
1149 verify(typefaceAdapter, times(1)).create(
1150 fontFamily = eq(fontFamily),
1151 fontWeight = eq(FontWeight.normal),
Siyamed Siniradf982d2019-05-20 17:35:13 -07001152 fontStyle = eq(FontStyle.Normal),
Siyamed Sinir934a3442019-06-18 12:57:13 -07001153 fontSynthesis = eq(FontSynthesis.All)
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001154 )
1155 val typeface = paragraph.textPaint.typeface
1156 assertThat(typeface.isBold, equalTo(false))
1157 assertThat(typeface.isItalic, equalTo(false))
1158 }
1159
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -08001160 @Test
1161 fun testEllipsis_withMaxLineEqualsNull_doesNotEllipsis() {
1162 val text = "abc"
1163 val fontSize = 20f
1164 val paragraphWidth = (text.length - 1) * fontSize
1165 val paragraph = simpleParagraph(
1166 text = text,
1167 fontFamily = fontFamily,
1168 fontSize = fontSize,
1169 ellipsis = true
1170 )
1171 paragraph.layout(paragraphWidth)
1172 for (i in 0 until paragraph.lineCount) {
1173 assertFalse(paragraph.isEllipsisApplied(i))
1174 }
1175 }
1176
1177 @Test
1178 fun testEllipsis_withMaxLinesLessThanTextLines_doesEllipsis() {
1179 val text = "abcde"
1180 val fontSize = 100f
1181 // Note that on API 21, if the next line only contains 1 character, ellipsis won't work
1182 val paragraphWidth = (text.length - 1.5f) * fontSize
1183 val paragraph = simpleParagraph(
1184 text = text,
1185 fontFamily = fontFamily,
1186 fontSize = fontSize,
1187 ellipsis = true,
1188 maxLines = 1
1189 )
1190 paragraph.layout(paragraphWidth)
1191
1192 assertTrue(paragraph.isEllipsisApplied(0))
1193 }
1194
1195 @Test
1196 fun testEllipsis_withMaxLinesMoreThanTextLines_doesNotEllipsis() {
1197 val text = "abc"
1198 val fontSize = 100f
1199 val paragraphWidth = (text.length - 1) * fontSize
1200 val maxLines = ceil(text.length * fontSize / paragraphWidth).toInt()
1201 val paragraph = simpleParagraph(
1202 text = text,
1203 fontFamily = fontFamily,
1204 fontSize = fontSize,
1205 ellipsis = true,
1206 maxLines = maxLines
1207 )
1208 paragraph.layout(paragraphWidth)
1209
1210 for (i in 0 until paragraph.lineCount) {
1211 assertFalse(paragraph.isEllipsisApplied(i))
1212 }
1213 }
1214
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001215 private fun simpleParagraph(
1216 text: CharSequence = "",
Haoyu Zhang47f57a22018-12-12 13:16:40 -08001217 textStyles: List<ParagraphBuilder.TextStyleIndex> = listOf(),
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001218 textAlign: TextAlign? = null,
George Mountb1cdd862018-12-21 10:54:42 -08001219 fontSize: Float? = null,
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -08001220 ellipsis: Boolean? = null,
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001221 maxLines: Int? = null,
1222 fontFamily: FontFamily? = null,
1223 fontWeight: FontWeight? = null,
1224 fontStyle: FontStyle? = null,
1225 typefaceAdapter: TypefaceAdapter = TypefaceAdapter()
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001226 ): ParagraphAndroid {
1227 return ParagraphAndroid(
1228 text = StringBuilder(text),
Haoyu Zhang47f57a22018-12-12 13:16:40 -08001229 textStyles = textStyles,
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001230 typefaceAdapter = typefaceAdapter,
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001231 paragraphStyle = ParagraphStyle(
1232 textAlign = textAlign,
Haoyu Zhangc11ef5a2019-01-14 16:35:05 -08001233 ellipsis = ellipsis,
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001234 maxLines = maxLines,
Siyamed Sinirf18dcca2018-12-20 18:25:10 -08001235 fontFamily = fontFamily,
1236 fontSize = fontSize,
1237 fontWeight = fontWeight,
1238 fontStyle = fontStyle
Haoyu Zhang10fc6502018-11-09 15:50:11 -08001239 )
1240 )
1241 }
George Mountb1cdd862018-12-21 10:54:42 -08001242}