valo_text/style.rs
1use valo_geometry::{Color, Point};
2
3/// `TextStyle` controls font selection and painting for a span of text.
4///
5/// Families are tried in order for each character before the collection's
6/// fallback fonts.
7#[derive(Clone, Debug, PartialEq)]
8pub struct TextStyle {
9 /// `families` lists preferred font families in fallback order.
10 pub families: Vec<String>,
11 /// `weight` selects a CSS font weight, conventionally from 100 to 900.
12 pub weight: u16,
13 /// `italic` selects an italic face when available.
14 pub italic: bool,
15 /// `stretch` selects CSS font width as a percentage, where 100 is normal.
16 ///
17 /// Valo selects a registered width and does not synthesize one.
18 pub stretch: f32,
19 /// `kerning` enables the font's kerning adjustments.
20 pub kerning: bool,
21 /// `variant_caps` selects OpenType capital-letter forms.
22 pub variant_caps: VariantCaps,
23 /// `size` is the font size in logical pixels.
24 pub size: f32,
25 /// `color` is the text fill color.
26 pub color: Color,
27 /// `letter_spacing` adds logical pixels after each grapheme cluster.
28 pub letter_spacing: f32,
29 /// `word_spacing` adds logical pixels after each space, in addition to letter spacing.
30 pub word_spacing: f32,
31 /// `height` overrides line height as a multiple of `size`.
32 ///
33 /// `None` uses the font's metrics.
34 pub height: Option<f32>,
35 /// `decoration` optionally adds an underline, overline, or strike-through.
36 pub decoration: Option<Decoration>,
37 /// `shadows` are painted back-to-front beneath the text.
38 pub shadows: Vec<Shadow>,
39}
40
41impl Default for TextStyle {
42 fn default() -> Self {
43 Self {
44 families: Vec::new(),
45 weight: 400,
46 italic: false,
47 stretch: crate::font::NORMAL_STRETCH,
48 kerning: true,
49 variant_caps: VariantCaps::Normal,
50 size: 14.0,
51 color: Color::BLACK,
52 letter_spacing: 0.0,
53 word_spacing: 0.0,
54 height: None,
55 decoration: None,
56 shadows: Vec::new(),
57 }
58 }
59}
60
61impl TextStyle {
62 /// `new` creates a style with one preferred family, size, and color.
63 pub fn new(family: &str, size: f32, color: Color) -> Self {
64 Self {
65 families: vec![family.to_owned()],
66 size,
67 color,
68 ..Default::default()
69 }
70 }
71
72 /// `font_attrs` returns the attributes used to select a face within a family.
73 pub fn font_attrs(&self) -> crate::font::FontAttrs {
74 crate::font::FontAttrs {
75 weight: self.weight,
76 italic: self.italic,
77 stretch: self.stretch,
78 }
79 }
80}
81
82/// `VariantCaps` selects an OpenType capitalization variant.
83///
84/// If a font lacks the requested feature, text remains unchanged; Valo does not
85/// synthesize capital forms.
86#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
87pub enum VariantCaps {
88 /// `Normal` leaves capitalization features disabled.
89 #[default]
90 Normal,
91 /// `SmallCaps` renders lowercase letters as small capitals.
92 SmallCaps,
93 /// `AllSmallCaps` renders both lowercase and uppercase letters as small capitals.
94 AllSmallCaps,
95 /// `PetiteCaps` renders lowercase letters as petite capitals.
96 PetiteCaps,
97 /// `AllPetiteCaps` renders both lowercase and uppercase letters as petite capitals.
98 AllPetiteCaps,
99 /// `Unicase` uses a mixture of uppercase and lowercase-sized capitals.
100 Unicase,
101 /// `TitlingCaps` uses capitals designed for display text.
102 TitlingCaps,
103}
104
105impl VariantCaps {
106 /// `feature_tags` returns the enabled OpenType tags in application order.
107 pub fn feature_tags(self) -> &'static [&'static [u8; 4]] {
108 match self {
109 Self::Normal => &[],
110 Self::SmallCaps => &[b"smcp"],
111 Self::AllSmallCaps => &[b"c2sc", b"smcp"],
112 Self::PetiteCaps => &[b"pcap"],
113 Self::AllPetiteCaps => &[b"c2pc", b"pcap"],
114 Self::Unicase => &[b"unic"],
115 Self::TitlingCaps => &[b"titl"],
116 }
117 }
118}
119
120/// `Decoration` describes a line drawn relative to styled text.
121#[derive(Clone, Copy, Debug, PartialEq)]
122pub struct Decoration {
123 /// `kind` selects the decoration's position.
124 pub kind: DecorationKind,
125 /// `color` overrides the text color when set.
126 pub color: Option<Color>,
127 /// `thickness` multiplies the font's suggested decoration thickness.
128 pub thickness: f32,
129}
130
131impl Decoration {
132 /// `new` creates a text-colored decoration at the font's suggested thickness.
133 pub fn new(kind: DecorationKind) -> Self {
134 Self {
135 kind,
136 color: None,
137 thickness: 1.0,
138 }
139 }
140}
141
142/// `DecorationKind` selects where a text decoration is drawn.
143#[derive(Clone, Copy, Debug, PartialEq, Eq)]
144pub enum DecorationKind {
145 /// `Underline` draws below the baseline using the font's underline metrics.
146 Underline,
147 /// `LineThrough` draws through the text using the font's strikeout metrics.
148 LineThrough,
149 /// `Overline` draws above the text.
150 Overline,
151}
152
153/// `Shadow` describes a colored, offset copy painted beneath text.
154#[derive(Clone, Copy, Debug, PartialEq)]
155pub struct Shadow {
156 /// `color` is the shadow color.
157 pub color: Color,
158 /// `offset` moves the shadow in logical pixels.
159 pub offset: Point,
160 /// `blur` is the Gaussian sigma; zero produces a sharp copy.
161 pub blur: f32,
162}
163
164/// `TextAlign` controls horizontal line placement within the layout width.
165#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
166pub enum TextAlign {
167 /// `Left` aligns lines with the left edge.
168 #[default]
169 Left,
170 /// `Center` centers each line.
171 Center,
172 /// `Right` aligns lines with the right edge.
173 Right,
174 /// `Justify` expands word spacing to fill eligible lines.
175 ///
176 /// Final lines and lines ending in a hard break remain unexpanded.
177 Justify,
178}
179
180impl From<TextAlign> for ParagraphStyle {
181 fn from(align: TextAlign) -> Self {
182 Self {
183 align,
184 ..Default::default()
185 }
186 }
187}
188
189/// `TextDirection` selects a paragraph's base writing direction.
190#[derive(Clone, Copy, Debug, PartialEq, Eq)]
191pub enum TextDirection {
192 /// `Ltr` sets a left-to-right base direction.
193 Ltr,
194 /// `Rtl` sets a right-to-left base direction.
195 Rtl,
196}
197
198/// `ParagraphStyle` controls layout behavior for a complete paragraph.
199#[derive(Clone, Debug, Default)]
200pub struct ParagraphStyle {
201 /// `align` controls horizontal line alignment.
202 pub align: TextAlign,
203 /// `direction` sets the bidirectional base direction.
204 ///
205 /// `None` infers it from the first strong character. Set it explicitly for
206 /// neutral text such as digits and punctuation.
207 pub direction: Option<TextDirection>,
208 /// `preserve_trailing_whitespace` includes trailing spaces in line widths.
209 pub preserve_trailing_whitespace: bool,
210 /// `max_lines` limits the number of laid-out lines.
211 pub max_lines: Option<u32>,
212 /// `ellipsis` replaces omitted content at the visual end of a truncated paragraph.
213 pub ellipsis: Option<String>,
214}