typst_library/text/deco.rs
1use crate::foundations::{Content, Smart, elem};
2use crate::introspection::{Locatable, Tagged};
3use crate::layout::{Abs, Corners, Length, Rel, Sides};
4use crate::text::{BottomEdge, BottomEdgeMetric, TopEdge, TopEdgeMetric};
5use crate::visualize::{Color, FixedStroke, Paint, Stroke};
6
7/// Underlines text.
8///
9/// # Example
10/// ```example
11/// This is #underline[important].
12/// ```
13#[elem(Locatable, Tagged)]
14pub struct UnderlineElem {
15 /// How to [stroke] the line.
16 ///
17 /// If set to `{auto}`, takes on the text's color and a thickness defined in
18 /// the current font.
19 ///
20 /// ```example
21 /// Take #underline(
22 /// stroke: 1.5pt + red,
23 /// offset: 2pt,
24 /// [care],
25 /// )
26 /// ```
27 #[fold]
28 pub stroke: Smart<Stroke>,
29
30 /// The position of the line relative to the baseline, read from the font
31 /// tables if `{auto}`.
32 ///
33 /// ```example
34 /// #underline(offset: 5pt)[
35 /// The Tale Of A Faraway Line I
36 /// ]
37 /// ```
38 pub offset: Smart<Length>,
39
40 /// The amount by which to extend the line beyond (or within if negative)
41 /// the content.
42 ///
43 /// ```example
44 /// #align(center,
45 /// underline(extent: 2pt)[Chapter 1]
46 /// )
47 /// ```
48 pub extent: Length,
49
50 /// Whether the line skips sections in which it would collide with the
51 /// glyphs.
52 ///
53 /// ```example
54 /// This #underline(evade: true)[is great].
55 /// This #underline(evade: false)[is less great].
56 /// ```
57 #[default(true)]
58 pub evade: bool,
59
60 /// Whether the line is placed behind the content it underlines.
61 ///
62 /// ```example
63 /// #set underline(stroke: (thickness: 1em, paint: maroon, cap: "round"))
64 /// #underline(background: true)[This is stylized.] \
65 /// #underline(background: false)[This is partially hidden.]
66 /// ```
67 #[default(false)]
68 pub background: bool,
69
70 /// The content to underline.
71 #[required]
72 pub body: Content,
73}
74
75/// Adds a line over text.
76///
77/// # Example
78/// ```example
79/// #overline[A line over text.]
80/// ```
81#[elem(Locatable, Tagged)]
82pub struct OverlineElem {
83 /// How to [stroke] the line.
84 ///
85 /// If set to `{auto}`, takes on the text's color and a thickness defined in
86 /// the current font.
87 ///
88 /// ```example
89 /// #set text(fill: olive)
90 /// #overline(
91 /// stroke: green.darken(20%),
92 /// offset: -12pt,
93 /// [The Forest Theme],
94 /// )
95 /// ```
96 #[fold]
97 pub stroke: Smart<Stroke>,
98
99 /// The position of the line relative to the baseline. Read from the font
100 /// tables if `{auto}`.
101 ///
102 /// ```example
103 /// #overline(offset: -1.2em)[
104 /// The Tale Of A Faraway Line II
105 /// ]
106 /// ```
107 pub offset: Smart<Length>,
108
109 /// The amount by which to extend the line beyond (or within if negative)
110 /// the content.
111 ///
112 /// ```example
113 /// #set overline(extent: 4pt)
114 /// #set underline(extent: 4pt)
115 /// #overline(underline[Typography Today])
116 /// ```
117 pub extent: Length,
118
119 /// Whether the line skips sections in which it would collide with the
120 /// glyphs.
121 ///
122 /// ```example
123 /// #overline(
124 /// evade: false,
125 /// offset: -7.5pt,
126 /// stroke: 1pt,
127 /// extent: 3pt,
128 /// [Temple],
129 /// )
130 /// ```
131 #[default(true)]
132 pub evade: bool,
133
134 /// Whether the line is placed behind the content it overlines.
135 ///
136 /// ```example
137 /// #set overline(stroke: (thickness: 1em, paint: maroon, cap: "round"))
138 /// #overline(background: true)[This is stylized.] \
139 /// #overline(background: false)[This is partially hidden.]
140 /// ```
141 #[default(false)]
142 pub background: bool,
143
144 /// The content to add a line over.
145 #[required]
146 pub body: Content,
147}
148
149/// Strikes through text.
150///
151/// # Example
152/// ```example
153/// This is #strike[not] relevant.
154/// ```
155#[elem(title = "Strikethrough", Locatable, Tagged)]
156pub struct StrikeElem {
157 /// How to [stroke] the line.
158 ///
159 /// If set to `{auto}`, takes on the text's color and a thickness defined in
160 /// the current font.
161 ///
162 /// _Note:_ Please don't use this for real redaction as you can still copy
163 /// paste the text.
164 ///
165 /// ```example
166 /// This is #strike(stroke: 1.5pt + red)[very stricken through]. \
167 /// This is #strike(stroke: 10pt)[redacted].
168 /// ```
169 #[fold]
170 pub stroke: Smart<Stroke>,
171
172 /// The position of the line relative to the baseline. Read from the font
173 /// tables if `{auto}`.
174 ///
175 /// This is useful if you are unhappy with the offset your font provides.
176 ///
177 /// ```example
178 /// #set text(font: "Inria Serif")
179 /// This is #strike(offset: auto)[low-ish]. \
180 /// This is #strike(offset: -3.5pt)[on-top].
181 /// ```
182 pub offset: Smart<Length>,
183
184 /// The amount by which to extend the line beyond (or within if negative)
185 /// the content.
186 ///
187 /// ```example
188 /// This #strike(extent: -2pt)[skips] parts of the word.
189 /// This #strike(extent: 2pt)[extends] beyond the word.
190 /// ```
191 pub extent: Length,
192
193 /// Whether the line is placed behind the content.
194 ///
195 /// ```example
196 /// #set strike(stroke: red)
197 /// #strike(background: true)[This is behind.] \
198 /// #strike(background: false)[This is in front.]
199 /// ```
200 #[default(false)]
201 pub background: bool,
202
203 /// The content to strike through.
204 #[required]
205 pub body: Content,
206}
207
208/// Highlights text with a background color.
209///
210/// # Example
211/// ```example
212/// This is #highlight[important].
213/// ```
214#[elem(Locatable, Tagged)]
215pub struct HighlightElem {
216 /// The color to highlight the text with.
217 ///
218 /// ```example
219 /// This is #highlight(
220 /// fill: blue
221 /// )[highlighted with blue].
222 /// ```
223 #[default(Some(Color::from_u8(0xFF, 0xFD, 0x11, 0xA1).into()))]
224 pub fill: Option<Paint>,
225
226 /// The highlight's border color. See the
227 /// [rectangle's documentation]($rect.stroke) for more details.
228 ///
229 /// ```example
230 /// This is a #highlight(
231 /// stroke: fuchsia
232 /// )[stroked highlighting].
233 /// ```
234 #[fold]
235 pub stroke: Sides<Option<Option<Stroke>>>,
236
237 /// The top end of the background rectangle.
238 ///
239 /// ```example
240 /// #set highlight(top-edge: "ascender")
241 /// #highlight[a] #highlight[aib]
242 ///
243 /// #set highlight(top-edge: "x-height")
244 /// #highlight[a] #highlight[aib]
245 /// ```
246 #[default(TopEdge::Metric(TopEdgeMetric::Ascender))]
247 pub top_edge: TopEdge,
248
249 /// The bottom end of the background rectangle.
250 ///
251 /// ```example
252 /// #set highlight(bottom-edge: "descender")
253 /// #highlight[a] #highlight[ap]
254 ///
255 /// #set highlight(bottom-edge: "baseline")
256 /// #highlight[a] #highlight[ap]
257 /// ```
258 #[default(BottomEdge::Metric(BottomEdgeMetric::Descender))]
259 pub bottom_edge: BottomEdge,
260
261 /// The amount by which to extend the background to the sides beyond
262 /// (or within if negative) the content.
263 ///
264 /// ```example
265 /// A long #highlight(extent: 4pt)[background].
266 /// ```
267 pub extent: Length,
268
269 /// How much to round the highlight's corners. See the
270 /// [rectangle's documentation]($rect.radius) for more details.
271 ///
272 /// ```example
273 /// Listen #highlight(
274 /// radius: 5pt, extent: 2pt
275 /// )[carefully], it will be on the test.
276 /// ```
277 #[fold]
278 pub radius: Corners<Option<Rel<Length>>>,
279
280 /// The content that should be highlighted.
281 #[required]
282 pub body: Content,
283}
284
285/// A text decoration.
286///
287/// Can be positioned over, under, or on top of text, or highlight the text with
288/// a background.
289#[derive(Debug, Clone, Eq, PartialEq, Hash)]
290pub struct Decoration {
291 pub line: DecoLine,
292 pub extent: Abs,
293}
294
295/// A kind of decorative line.
296#[derive(Debug, Clone, Eq, PartialEq, Hash)]
297#[allow(clippy::large_enum_variant)]
298pub enum DecoLine {
299 Underline {
300 stroke: Stroke<Abs>,
301 offset: Smart<Abs>,
302 evade: bool,
303 background: bool,
304 },
305 Strikethrough {
306 stroke: Stroke<Abs>,
307 offset: Smart<Abs>,
308 background: bool,
309 },
310 Overline {
311 stroke: Stroke<Abs>,
312 offset: Smart<Abs>,
313 evade: bool,
314 background: bool,
315 },
316 Highlight {
317 fill: Option<Paint>,
318 stroke: Sides<Option<FixedStroke>>,
319 top_edge: TopEdge,
320 bottom_edge: BottomEdge,
321 radius: Corners<Rel<Abs>>,
322 },
323}