0
# Text Handling and Typography
1
2
Complete text system with styling, input capabilities, and internationalization support for creating rich text experiences across all platforms.
3
4
## Capabilities
5
6
### Text Styling
7
8
Comprehensive text appearance configuration system supporting fonts, colors, spacing, and advanced typography features.
9
10
```kotlin { .api }
11
/**
12
* Configuration object to define the styling of text content.
13
*/
14
@Immutable
15
data class TextStyle(
16
val color: Color = Color.Unspecified,
17
val fontSize: TextUnit = TextUnit.Unspecified,
18
val fontWeight: FontWeight? = null,
19
val fontStyle: FontStyle? = null,
20
val fontSynthesis: FontSynthesis? = null,
21
val fontFamily: FontFamily? = null,
22
val fontFeatureSettings: String? = null,
23
val letterSpacing: TextUnit = TextUnit.Unspecified,
24
val baselineShift: BaselineShift? = null,
25
val textGeometricTransform: TextGeometricTransform? = null,
26
val localeList: LocaleList? = null,
27
val background: Color = Color.Unspecified,
28
val textDecoration: TextDecoration? = null,
29
val shadow: Shadow? = null,
30
val textAlign: TextAlign? = null,
31
val textDirection: TextDirection? = null,
32
val lineHeight: TextUnit = TextUnit.Unspecified,
33
val textIndent: TextIndent? = null,
34
val platformStyle: PlatformTextStyle? = null,
35
val lineHeightStyle: LineHeightStyle? = null,
36
val lineBreak: LineBreak? = null,
37
val hyphens: Hyphens? = null
38
) {
39
/**
40
* Returns a new text style that is a combination of this style and the given other style.
41
*/
42
fun merge(other: TextStyle? = null): TextStyle
43
44
/**
45
* Plus operator overload that applies a merge.
46
*/
47
operator fun plus(other: TextStyle): TextStyle
48
49
/**
50
* Returns a copy of this TextStyle with the given values replaced.
51
*/
52
fun copy(
53
color: Color = this.color,
54
fontSize: TextUnit = this.fontSize,
55
fontWeight: FontWeight? = this.fontWeight,
56
fontStyle: FontStyle? = this.fontStyle,
57
fontSynthesis: FontSynthesis? = this.fontSynthesis,
58
fontFamily: FontFamily? = this.fontFamily,
59
fontFeatureSettings: String? = this.fontFeatureSettings,
60
letterSpacing: TextUnit = this.letterSpacing,
61
baselineShift: BaselineShift? = this.baselineShift,
62
textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
63
localeList: LocaleList? = this.localeList,
64
background: Color = this.background,
65
textDecoration: TextDecoration? = this.textDecoration,
66
shadow: Shadow? = this.shadow,
67
textAlign: TextAlign? = this.textAlign,
68
textDirection: TextDirection? = this.textDirection,
69
lineHeight: TextUnit = this.lineHeight,
70
textIndent: TextIndent? = this.textIndent,
71
platformStyle: PlatformTextStyle? = this.platformStyle,
72
lineHeightStyle: LineHeightStyle? = this.lineHeightStyle,
73
lineBreak: LineBreak? = this.lineBreak,
74
hyphens: Hyphens? = this.hyphens
75
): TextStyle
76
77
companion object {
78
/**
79
* Constant for default text style.
80
*/
81
val Default: TextStyle
82
}
83
}
84
85
/**
86
* Font weight values from thin to black.
87
*/
88
@Immutable
89
class FontWeight private constructor(val weight: Int) : Comparable<FontWeight> {
90
override fun compareTo(other: FontWeight): Int
91
92
companion object {
93
val Thin: FontWeight // 100
94
val ExtraLight: FontWeight // 200
95
val Light: FontWeight // 300
96
val Normal: FontWeight // 400
97
val Medium: FontWeight // 500
98
val SemiBold: FontWeight // 600
99
val Bold: FontWeight // 700
100
val ExtraBold: FontWeight // 800
101
val Black: FontWeight // 900
102
103
/**
104
* Create a FontWeight with a specific weight value.
105
*/
106
fun W100: FontWeight
107
fun W200: FontWeight
108
fun W300: FontWeight
109
fun W400: FontWeight
110
fun W500: FontWeight
111
fun W600: FontWeight
112
fun W700: FontWeight
113
fun W800: FontWeight
114
fun W900: FontWeight
115
}
116
}
117
118
/**
119
* Font style enumeration for normal and italic text.
120
*/
121
enum class FontStyle(val value: Int) {
122
Normal(0), Italic(1)
123
}
124
125
/**
126
* Font synthesis options for generating synthetic font variants.
127
*/
128
@Immutable
129
class FontSynthesis private constructor(val value: Int) {
130
companion object {
131
val None: FontSynthesis
132
val All: FontSynthesis
133
val Weight: FontSynthesis
134
val Style: FontSynthesis
135
}
136
}
137
```
138
139
**Usage Examples:**
140
141
```kotlin
142
// Basic text styling
143
val titleStyle = TextStyle(
144
fontSize = 24.sp,
145
fontWeight = FontWeight.Bold,
146
color = Color.Black,
147
letterSpacing = 0.5.sp
148
)
149
150
val bodyStyle = TextStyle(
151
fontSize = 16.sp,
152
fontWeight = FontWeight.Normal,
153
lineHeight = 24.sp,
154
color = Color.DarkGray
155
)
156
157
// Merging styles
158
val emphasisStyle = bodyStyle.copy(
159
fontWeight = FontWeight.SemiBold,
160
fontStyle = FontStyle.Italic
161
)
162
163
// Using styles in Text composables
164
Text(
165
text = "Hello, World!",
166
style = titleStyle
167
)
168
169
Text(
170
text = "This is body text with custom styling.",
171
style = bodyStyle.merge(
172
TextStyle(textDecoration = TextDecoration.Underline)
173
)
174
)
175
```
176
177
### Font Management
178
179
Font family definition and loading system supporting platform-specific fonts and custom font resources.
180
181
```kotlin { .api }
182
/**
183
* The base class of font families.
184
*/
185
abstract class FontFamily(
186
val canLoadSynchronously: Boolean
187
) {
188
companion object {
189
/**
190
* The platform default font.
191
*/
192
val Default: SystemFontFamily
193
194
/**
195
* Generic serif font family.
196
*/
197
val Serif: GenericFontFamily
198
199
/**
200
* Generic sans-serif font family.
201
*/
202
val SansSerif: GenericFontFamily
203
204
/**
205
* Generic monospace font family.
206
*/
207
val Monospace: GenericFontFamily
208
209
/**
210
* Generic cursive font family.
211
*/
212
val Cursive: GenericFontFamily
213
}
214
}
215
216
/**
217
* Defines a font family with explicitly defined fonts.
218
*/
219
class FontFamily private constructor(
220
val fonts: List<Font>
221
) : FontFamily(false) {
222
constructor(vararg fonts: Font) : this(fonts.toList())
223
constructor(font: Font) : this(listOf(font))
224
}
225
226
/**
227
* Base class for font definitions.
228
*/
229
abstract class Font {
230
/**
231
* The weight of the font.
232
*/
233
abstract val weight: FontWeight
234
235
/**
236
* The style of the font.
237
*/
238
abstract val style: FontStyle
239
240
/**
241
* Load result for font loading.
242
*/
243
sealed class LoadResult {
244
class Success(val typeface: Any) : LoadResult()
245
class Failure(val exception: Exception) : LoadResult()
246
}
247
}
248
249
/**
250
* Create a font from a resource.
251
*/
252
expect fun Font(
253
resId: Int,
254
weight: FontWeight = FontWeight.Normal,
255
style: FontStyle = FontStyle.Normal
256
): Font
257
258
/**
259
* Create a font from a file path.
260
*/
261
expect fun Font(
262
path: String,
263
weight: FontWeight = FontWeight.Normal,
264
style: FontStyle = FontStyle.Normal
265
): Font
266
267
/**
268
* Create a font from byte array.
269
*/
270
expect fun Font(
271
data: ByteArray,
272
weight: FontWeight = FontWeight.Normal,
273
style: FontStyle = FontStyle.Normal
274
): Font
275
276
/**
277
* Font loading with suspending functions.
278
*/
279
@Composable
280
fun rememberFontFamily(vararg fonts: Font): State<FontFamily?>
281
```
282
283
**Usage Examples:**
284
285
```kotlin
286
// Using system font families
287
val titleFamily = FontFamily.Serif
288
val bodyFamily = FontFamily.SansSerif
289
val codeFamily = FontFamily.Monospace
290
291
// Creating custom font family (platform-specific)
292
val customFontFamily = FontFamily(
293
Font(R.font.my_regular_font, FontWeight.Normal),
294
Font(R.font.my_bold_font, FontWeight.Bold),
295
Font(R.font.my_italic_font, FontWeight.Normal, FontStyle.Italic)
296
)
297
298
// Using fonts in text styles
299
val customTextStyle = TextStyle(
300
fontFamily = customFontFamily,
301
fontSize = 18.sp,
302
fontWeight = FontWeight.Bold
303
)
304
305
// Loading fonts asynchronously
306
@Composable
307
fun CustomText() {
308
val fontFamily by rememberFontFamily(
309
Font("path/to/font.ttf", FontWeight.Normal)
310
)
311
312
fontFamily?.let { family ->
313
Text(
314
text = "Custom font text",
315
style = TextStyle(fontFamily = family)
316
)
317
}
318
}
319
```
320
321
### Text Layout and Alignment
322
323
Text positioning, alignment, and layout configuration for precise text placement and appearance.
324
325
```kotlin { .api }
326
/**
327
* Text alignment within its container.
328
*/
329
enum class TextAlign {
330
Left, Right, Center, Justify, Start, End
331
}
332
333
/**
334
* Text direction for bidirectional text support.
335
*/
336
enum class TextDirection {
337
Ltr, Rtl, Content, ContentOrLtr, ContentOrRtl
338
}
339
340
/**
341
* Text overflow handling strategies.
342
*/
343
enum class TextOverflow {
344
Clip, Ellipsis, Visible
345
}
346
347
/**
348
* Text decoration options.
349
*/
350
@Immutable
351
class TextDecoration private constructor(val mask: Int) {
352
operator fun plus(decoration: TextDecoration): TextDecoration
353
operator fun contains(other: TextDecoration): Boolean
354
355
companion object {
356
val None: TextDecoration
357
val Underline: TextDecoration
358
val LineThrough: TextDecoration
359
360
/**
361
* Combine multiple text decorations.
362
*/
363
fun combine(decorations: List<TextDecoration>): TextDecoration
364
}
365
}
366
367
/**
368
* Text shadow configuration.
369
*/
370
@Immutable
371
data class Shadow(
372
val color: Color = Color.Black,
373
val offset: Offset = Offset.Zero,
374
val blurRadius: Float = 0.0f
375
) {
376
companion object {
377
val None: Shadow
378
}
379
}
380
381
/**
382
* Text indentation configuration.
383
*/
384
@Immutable
385
data class TextIndent(
386
val firstLine: TextUnit = 0.sp,
387
val restLine: TextUnit = 0.sp
388
) {
389
companion object {
390
val None: TextIndent
391
}
392
}
393
394
/**
395
* Baseline shift for subscript and superscript text.
396
*/
397
@Immutable
398
class BaselineShift private constructor(val multiplier: Float) {
399
companion object {
400
val None: BaselineShift
401
val Subscript: BaselineShift
402
val Superscript: BaselineShift
403
}
404
}
405
406
/**
407
* Text geometric transformations.
408
*/
409
@Immutable
410
data class TextGeometricTransform(
411
val scaleX: Float = 1.0f,
412
val skewX: Float = 0.0f
413
) {
414
companion object {
415
val None: TextGeometricTransform
416
}
417
}
418
```
419
420
**Usage Examples:**
421
422
```kotlin
423
// Text alignment examples
424
Text(
425
text = "Left aligned text",
426
style = TextStyle(textAlign = TextAlign.Left),
427
modifier = Modifier.fillMaxWidth()
428
)
429
430
Text(
431
text = "Centered text",
432
style = TextStyle(textAlign = TextAlign.Center),
433
modifier = Modifier.fillMaxWidth()
434
)
435
436
// Text with decorations
437
Text(
438
text = "Underlined text",
439
style = TextStyle(
440
textDecoration = TextDecoration.Underline,
441
color = Color.Blue
442
)
443
)
444
445
// Text with shadow
446
Text(
447
text = "Shadow text",
448
style = TextStyle(
449
shadow = Shadow(
450
color = Color.Gray,
451
offset = Offset(2.0f, 2.0f),
452
blurRadius = 4.0f
453
),
454
fontSize = 20.sp
455
)
456
)
457
458
// Subscript and superscript
459
Text(
460
text = buildAnnotatedString {
461
append("H")
462
withStyle(
463
style = SpanStyle(
464
baselineShift = BaselineShift.Subscript,
465
fontSize = 12.sp
466
)
467
) {
468
append("2")
469
}
470
append("O")
471
}
472
)
473
```
474
475
### Text Input System
476
477
Comprehensive text input handling with field state management, validation, and input method control.
478
479
```kotlin { .api }
480
/**
481
* Value holder for TextField that contains text, cursor position, and selection range.
482
*/
483
@Immutable
484
class TextFieldValue(
485
val text: String,
486
val selection: TextRange = TextRange.Zero,
487
val composition: TextRange? = null
488
) {
489
/**
490
* Returns a copy of this TextFieldValue with the given values replaced.
491
*/
492
fun copy(
493
text: String = this.text,
494
selection: TextRange = this.selection,
495
composition: TextRange? = this.composition
496
): TextFieldValue
497
498
companion object {
499
/**
500
* Default TextFieldValue with empty text.
501
*/
502
val Saver: Saver<TextFieldValue, Any>
503
}
504
}
505
506
/**
507
* Text range for selection and composition.
508
*/
509
@Immutable
510
data class TextRange(
511
val start: Int,
512
val end: Int
513
) {
514
/**
515
* The minimum offset of the range.
516
*/
517
val min: Int
518
519
/**
520
* The maximum offset of the range.
521
*/
522
val max: Int
523
524
/**
525
* The length of the range.
526
*/
527
val length: Int
528
529
/**
530
* Whether this range represents a collapsed selection.
531
*/
532
val collapsed: Boolean
533
534
/**
535
* Whether this range represents a reversed selection.
536
*/
537
val reversed: Boolean
538
539
companion object {
540
val Zero: TextRange
541
}
542
}
543
544
/**
545
* Keyboard types for different input contexts.
546
*/
547
enum class KeyboardType {
548
Text, Ascii, Number, Phone, Uri, Email, Password, NumberPassword
549
}
550
551
/**
552
* Input method editor (IME) actions.
553
*/
554
enum class ImeAction {
555
Default, None, Go, Search, Send, Previous, Next, Done
556
}
557
558
/**
559
* Capitalization strategies for text input.
560
*/
561
enum class KeyboardCapitalization {
562
None, Characters, Words, Sentences
563
}
564
565
/**
566
* Keyboard options for configuring text input behavior.
567
*/
568
@Immutable
569
data class KeyboardOptions(
570
val capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
571
val autoCorrect: Boolean = true,
572
val keyboardType: KeyboardType = KeyboardType.Text,
573
val imeAction: ImeAction = ImeAction.Default
574
) {
575
companion object {
576
val Default: KeyboardOptions
577
}
578
}
579
580
/**
581
* Visual transformation for text input display.
582
*/
583
fun interface VisualTransformation {
584
/**
585
* Transform the text for display.
586
*/
587
fun filter(text: AnnotatedString): TransformedText
588
589
companion object {
590
/**
591
* No transformation applied.
592
*/
593
val None: VisualTransformation
594
}
595
}
596
597
/**
598
* Password visual transformation that masks input.
599
*/
600
class PasswordVisualTransformation(
601
val mask: Char = '\u2022'
602
) : VisualTransformation {
603
override fun filter(text: AnnotatedString): TransformedText
604
}
605
606
/**
607
* Result of visual transformation.
608
*/
609
class TransformedText(
610
val text: AnnotatedString,
611
val offsetMapping: OffsetMapping
612
)
613
614
/**
615
* Maps character offsets between original and transformed text.
616
*/
617
interface OffsetMapping {
618
/**
619
* Convert offset from original to transformed text.
620
*/
621
fun originalToTransformed(offset: Int): Int
622
623
/**
624
* Convert offset from transformed to original text.
625
*/
626
fun transformedToOriginal(offset: Int): Int
627
628
companion object {
629
/**
630
* Identity mapping (no transformation).
631
*/
632
val Identity: OffsetMapping
633
}
634
}
635
```
636
637
**Usage Examples:**
638
639
```kotlin
640
// Basic text field state
641
var textState by remember { mutableStateOf(TextFieldValue("")) }
642
643
TextField(
644
value = textState,
645
onValueChange = { textState = it },
646
label = { Text("Enter text") }
647
)
648
649
// Password field with visual transformation
650
var passwordState by remember { mutableStateOf("") }
651
652
TextField(
653
value = passwordState,
654
onValueChange = { passwordState = it },
655
label = { Text("Password") },
656
visualTransformation = PasswordVisualTransformation(),
657
keyboardOptions = KeyboardOptions(
658
keyboardType = KeyboardType.Password,
659
imeAction = ImeAction.Done
660
)
661
)
662
663
// Number input field
664
var numberState by remember { mutableStateOf("") }
665
666
TextField(
667
value = numberState,
668
onValueChange = { numberState = it },
669
label = { Text("Enter number") },
670
keyboardOptions = KeyboardOptions(
671
keyboardType = KeyboardType.Number,
672
imeAction = ImeAction.Next
673
)
674
)
675
676
// Email input with auto-correction disabled
677
var emailState by remember { mutableStateOf("") }
678
679
TextField(
680
value = emailState,
681
onValueChange = { emailState = it },
682
label = { Text("Email address") },
683
keyboardOptions = KeyboardOptions(
684
keyboardType = KeyboardType.Email,
685
autoCorrect = false,
686
capitalization = KeyboardCapitalization.None
687
)
688
)
689
```
690
691
### Annotated Text
692
693
Rich text system supporting spans, styles, and clickable text segments for complex text formatting.
694
695
```kotlin { .api }
696
/**
697
* Text with multiple styles and annotations.
698
*/
699
@Immutable
700
class AnnotatedString(
701
val text: String,
702
val spanStyles: List<Range<SpanStyle>> = emptyList(),
703
val paragraphStyles: List<Range<ParagraphStyle>> = emptyList(),
704
val annotations: List<Range<out Any>> = emptyList()
705
) : CharSequence {
706
707
override val length: Int
708
override fun get(index: Int): Char
709
override fun subSequence(startIndex: Int, endIndex: Int): AnnotatedString
710
711
/**
712
* Return a substring of this AnnotatedString.
713
*/
714
fun substring(startIndex: Int, endIndex: Int = length): AnnotatedString
715
716
/**
717
* Plus operator for concatenating AnnotatedStrings.
718
*/
719
operator fun plus(other: AnnotatedString): AnnotatedString
720
721
/**
722
* Returns a list of annotations attached to this AnnotatedString.
723
*/
724
fun <T : Any> getStringAnnotations(tag: String, start: Int, end: Int): List<Range<T>>
725
726
/**
727
* Range class for associating data with text ranges.
728
*/
729
@Immutable
730
data class Range<T>(
731
val item: T,
732
val start: Int,
733
val end: Int,
734
val tag: String = ""
735
)
736
737
companion object {
738
val Saver: Saver<AnnotatedString, Any>
739
}
740
}
741
742
/**
743
* Styling information that can be applied to a span of text.
744
*/
745
@Immutable
746
data class SpanStyle(
747
val color: Color = Color.Unspecified,
748
val fontSize: TextUnit = TextUnit.Unspecified,
749
val fontWeight: FontWeight? = null,
750
val fontStyle: FontStyle? = null,
751
val fontSynthesis: FontSynthesis? = null,
752
val fontFamily: FontFamily? = null,
753
val fontFeatureSettings: String? = null,
754
val letterSpacing: TextUnit = TextUnit.Unspecified,
755
val baselineShift: BaselineShift? = null,
756
val textGeometricTransform: TextGeometricTransform? = null,
757
val localeList: LocaleList? = null,
758
val background: Color = Color.Unspecified,
759
val textDecoration: TextDecoration? = null,
760
val shadow: Shadow? = null,
761
val platformStyle: PlatformSpanStyle? = null
762
) {
763
/**
764
* Returns a new span style that is a combination of this style and the given other style.
765
*/
766
fun merge(other: SpanStyle? = null): SpanStyle
767
768
/**
769
* Plus operator overload that applies a merge.
770
*/
771
operator fun plus(other: SpanStyle): SpanStyle
772
}
773
774
/**
775
* Styling information for paragraphs.
776
*/
777
@Immutable
778
data class ParagraphStyle(
779
val textAlign: TextAlign? = null,
780
val textDirection: TextDirection? = null,
781
val lineHeight: TextUnit = TextUnit.Unspecified,
782
val textIndent: TextIndent? = null,
783
val platformStyle: PlatformParagraphStyle? = null,
784
val lineHeightStyle: LineHeightStyle? = null,
785
val lineBreak: LineBreak? = null,
786
val hyphens: Hyphens? = null
787
) {
788
/**
789
* Returns a new paragraph style that is a combination of this style and the given other style.
790
*/
791
fun merge(other: ParagraphStyle? = null): ParagraphStyle
792
793
/**
794
* Plus operator overload that applies a merge.
795
*/
796
operator fun plus(other: ParagraphStyle): ParagraphStyle
797
}
798
799
/**
800
* Builder for creating AnnotatedStrings with styled content.
801
*/
802
class AnnotatedString.Builder(capacity: Int = 16) {
803
/**
804
* Current length of the built string.
805
*/
806
val length: Int
807
808
/**
809
* Append text to the builder.
810
*/
811
fun append(text: String)
812
fun append(text: Char)
813
fun append(text: AnnotatedString)
814
815
/**
816
* Apply a SpanStyle to the given range.
817
*/
818
fun addStyle(style: SpanStyle, start: Int, end: Int)
819
820
/**
821
* Apply a ParagraphStyle to the given range.
822
*/
823
fun addStyle(style: ParagraphStyle, start: Int, end: Int)
824
825
/**
826
* Add an annotation to the given range.
827
*/
828
fun addStringAnnotation(tag: String, annotation: String, start: Int, end: Int)
829
830
/**
831
* Apply styles within a scope.
832
*/
833
inline fun withStyle(style: SpanStyle, action: AnnotatedString.Builder.() -> Unit): Int
834
inline fun withStyle(style: ParagraphStyle, action: AnnotatedString.Builder.() -> Unit): Int
835
836
/**
837
* Build the final AnnotatedString.
838
*/
839
fun toAnnotatedString(): AnnotatedString
840
}
841
842
/**
843
* DSL for building AnnotatedString.
844
*/
845
inline fun buildAnnotatedString(
846
builder: AnnotatedString.Builder.() -> Unit
847
): AnnotatedString
848
```
849
850
**Usage Examples:**
851
852
```kotlin
853
// Creating rich text with annotations
854
val richText = buildAnnotatedString {
855
append("Visit our ")
856
857
// Add clickable link
858
pushStringAnnotation(tag = "URL", annotation = "https://example.com")
859
withStyle(style = SpanStyle(color = Color.Blue, textDecoration = TextDecoration.Underline)) {
860
append("website")
861
}
862
pop()
863
864
append(" for more information.\n\n")
865
866
// Add emphasized text
867
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, fontSize = 18.sp)) {
868
append("Important Notice")
869
}
870
871
append("\nThis text contains ")
872
873
// Add styled span
874
withStyle(style = SpanStyle(
875
color = Color.Red,
876
fontStyle = FontStyle.Italic,
877
background = Color.Yellow.copy(alpha = 0.3f)
878
)) {
879
append("highlighted content")
880
}
881
882
append(" within the paragraph.")
883
}
884
885
// Using the rich text in a Text composable
886
ClickableText(
887
text = richText,
888
onClick = { offset ->
889
richText.getStringAnnotations(tag = "URL", start = offset, end = offset)
890
.firstOrNull()?.let { annotation ->
891
// Handle URL click
892
println("Clicked URL: ${annotation.item}")
893
}
894
}
895
)
896
897
// Multi-styled paragraph
898
val styledText = buildAnnotatedString {
899
withStyle(style = ParagraphStyle(textAlign = TextAlign.Center)) {
900
withStyle(style = SpanStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold)) {
901
append("Centered Title")
902
}
903
}
904
905
append("\n\n")
906
907
withStyle(style = ParagraphStyle(textAlign = TextAlign.Justify, lineHeight = 20.sp)) {
908
append("This is a justified paragraph with custom line height. ")
909
910
withStyle(style = SpanStyle(fontWeight = FontWeight.SemiBold)) {
911
append("Bold text")
912
}
913
914
append(" mixed with ")
915
916
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic)) {
917
append("italic text")
918
}
919
920
append(" in the same paragraph.")
921
}
922
}
923
```