typst_library/text/shift.rs
1use crate::introspection::Tagged;
2use ttf_parser::Tag;
3
4use crate::foundations::{Content, Smart, elem};
5use crate::layout::{Em, Length};
6use crate::text::{FontMetrics, ScriptMetrics, TextSize};
7
8/// Renders text in subscript.
9///
10/// The text is rendered smaller and its baseline is lowered.
11///
12/// = Example <example>
13/// ```example
14/// Revenue#sub[yearly]
15/// ```
16#[elem(title = "Subscript", Tagged)]
17pub struct SubElem {
18 /// Whether to use subscript glyphs from the font if available.
19 ///
20 /// Ideally, subscripts glyphs are provided by the font (using the `subs`
21 /// OpenType feature). Otherwise, Typst is able to synthesize subscripts by
22 /// lowering and scaling down regular glyphs.
23 ///
24 /// When this is set to `{false}`, synthesized glyphs will be used
25 /// regardless of whether the font provides dedicated subscript glyphs. When
26 /// `{true}`, synthesized glyphs may still be used in case the font does not
27 /// provide the necessary subscript glyphs.
28 ///
29 /// ```example
30 /// N#sub(typographic: true)[1]
31 /// N#sub(typographic: false)[1]
32 /// ```
33 #[default(true)]
34 pub typographic: bool,
35
36 /// The downward baseline shift for synthesized subscripts.
37 ///
38 /// This only applies to synthesized subscripts. In other words, this has no
39 /// effect if `typographic` is `{true}` and the font provides the necessary
40 /// subscript glyphs.
41 ///
42 /// If set to `{auto}`, the baseline is shifted according to the metrics
43 /// provided by the font, with a fallback to `{0.2em}` in case the font does
44 /// not define the necessary metrics.
45 ///
46 /// When using multiple fonts, it might be necessary to set `baseline` and
47 /// @sub.size[`size`] explicitly. See @super.baseline[`super`] for an
48 /// example.
49 pub baseline: Smart<Length>,
50
51 /// The font size for synthesized subscripts.
52 ///
53 /// This only applies to synthesized subscripts. In other words, this has no
54 /// effect if `typographic` is `{true}` and the font provides the necessary
55 /// subscript glyphs.
56 ///
57 /// If set to `{auto}`, the size is scaled according to the metrics provided
58 /// by the font, with a fallback to `{0.6em}` in case the font does not
59 /// define the necessary metrics.
60 pub size: Smart<TextSize>,
61
62 /// The text to display in subscript.
63 #[required]
64 pub body: Content,
65}
66
67/// Renders text in superscript.
68///
69/// The text is rendered smaller and its baseline is raised.
70///
71/// = Example <example>
72/// ```example
73/// 1#super[st] try!
74/// ```
75#[elem(title = "Superscript", Tagged)]
76pub struct SuperElem {
77 /// Whether to use superscript glyphs from the font if available.
78 ///
79 /// Ideally, superscripts glyphs are provided by the font (using the `sups`
80 /// OpenType feature). Otherwise, Typst is able to synthesize superscripts
81 /// by raising and scaling down regular glyphs.
82 ///
83 /// When this is set to `{false}`, synthesized glyphs will be used
84 /// regardless of whether the font provides dedicated superscript glyphs.
85 /// When `{true}`, synthesized glyphs may still be used in case the font
86 /// does not provide the necessary superscript glyphs.
87 ///
88 /// ```example
89 /// N#super(typographic: true)[1]
90 /// N#super(typographic: false)[1]
91 /// ```
92 #[default(true)]
93 pub typographic: bool,
94
95 /// The downward baseline shift for synthesized superscripts.
96 ///
97 /// This only applies to synthesized superscripts. In other words, this has
98 /// no effect if `typographic` is `{true}` and the font provides the
99 /// necessary superscript glyphs.
100 ///
101 /// If set to `{auto}`, the baseline is shifted according to the metrics
102 /// provided by the font, with a fallback to `{-0.5em}` in case the font
103 /// does not define the necessary metrics.
104 ///
105 /// Note that, since the baseline shift is applied downward, you will need
106 /// to provide a negative value for the content to appear as raised above
107 /// the normal baseline.
108 ///
109 /// Sometimes it is necessary to set `baseline` and @super.size[`size`]
110 /// explicitly. In the following example, the superscripted text uses
111 /// multiple fonts with incompatible metrics. To avoid misalignment, we
112 /// override the metrics for all fonts.
113 ///
114 /// ```example
115 /// #let tz(timezone) = text(
116 /// font: "Roboto",
117 /// smallcaps(timezone, all: true),
118 /// )
119 ///
120 /// / Per-font metrics:
121 /// 14:00#super[#tz[UTC], not #tz[CET]]
122 ///
123 /// #set super(
124 /// baseline: -0.4em,
125 /// size: 0.6em,
126 /// typographic: false,
127 /// )
128 /// / Unified metrics:
129 /// 14:00#super[#tz[UTC], not #tz[CET]]
130 /// ```
131 pub baseline: Smart<Length>,
132
133 /// The font size for synthesized superscripts.
134 ///
135 /// This only applies to synthesized superscripts. In other words, this has
136 /// no effect if `typographic` is `{true}` and the font provides the
137 /// necessary superscript glyphs.
138 ///
139 /// If set to `{auto}`, the size is scaled according to the metrics provided
140 /// by the font, with a fallback to `{0.6em}` in case the font does not
141 /// define the necessary metrics.
142 pub size: Smart<TextSize>,
143
144 /// The text to display in superscript.
145 #[required]
146 pub body: Content,
147}
148
149/// Configuration values for sub- or superscript text.
150#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
151pub struct ShiftSettings {
152 /// Whether the OpenType feature should be used if possible.
153 pub typographic: bool,
154 /// The baseline shift of the script, relative to the outer text size.
155 ///
156 /// For superscripts, this is positive. For subscripts, this is negative. A
157 /// value of [`Smart::Auto`] indicates that the value should be obtained
158 /// from font metrics.
159 pub shift: Smart<Em>,
160 /// The size of the script, relative to the outer text size.
161 ///
162 /// A value of [`Smart::Auto`] indicates that the value should be obtained
163 /// from font metrics.
164 pub size: Smart<Em>,
165 /// The kind of script (either a subscript, or a superscript).
166 ///
167 /// This is used to know which OpenType table to use to resolve
168 /// [`Smart::Auto`] values.
169 pub kind: ScriptKind,
170}
171
172#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
173pub enum ScriptKind {
174 Sub,
175 Super,
176}
177
178impl ScriptKind {
179 /// Returns the default metrics for this script kind.
180 ///
181 /// This can be used as a last resort if neither the user nor the font
182 /// provided those metrics.
183 pub fn default_metrics(self) -> &'static ScriptMetrics {
184 match self {
185 Self::Sub => &DEFAULT_SUBSCRIPT_METRICS,
186 Self::Super => &DEFAULT_SUPERSCRIPT_METRICS,
187 }
188 }
189
190 /// Reads the script metrics from the font table for to this script kind.
191 pub fn read_metrics(self, font_metrics: &FontMetrics) -> &ScriptMetrics {
192 match self {
193 Self::Sub => font_metrics.subscript.as_ref(),
194 Self::Super => font_metrics.superscript.as_ref(),
195 }
196 .unwrap_or(self.default_metrics())
197 }
198
199 /// The corresponding OpenType feature.
200 pub const fn feature(self) -> Tag {
201 match self {
202 Self::Sub => Tag::from_bytes(b"subs"),
203 Self::Super => Tag::from_bytes(b"sups"),
204 }
205 }
206}
207pub static DEFAULT_SUBSCRIPT_METRICS: ScriptMetrics = ScriptMetrics {
208 width: Em::new(0.6),
209 height: Em::new(0.6),
210 horizontal_offset: Em::zero(),
211 vertical_offset: Em::new(-0.2),
212};
213
214pub static DEFAULT_SUPERSCRIPT_METRICS: ScriptMetrics = ScriptMetrics {
215 width: Em::new(0.6),
216 height: Em::new(0.6),
217 horizontal_offset: Em::zero(),
218 vertical_offset: Em::new(0.5),
219};