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}