Skip to main content

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};