use crate::diag::SourceResult; use crate::engine::Engine; use crate::foundations::{elem, Content, Packed, Show, Smart, StyleChain}; use crate::layout::{Em, Length}; use crate::text::{FontMetrics, TextElem, TextSize}; use ttf_parser::Tag; use typst_library::text::ScriptMetrics; /// Renders text in subscript. /// /// The text is rendered smaller and its baseline is lowered. /// /// # Example /// ```example /// Revenue#sub[yearly] /// ``` #[elem(title = "Subscript", Show)] pub struct SubElem { /// Whether to create artificial subscripts by shifting and scaling down /// regular glyphs. /// /// Ideally, subscripts glyphs should be provided by the font (using the /// `subs` OpenType feature). Otherwise, Typst is able to synthesize /// subscripts as explained above. /// /// When this is set to `{false}`, synthesized glyphs will be used /// regardless of whether the font provides dedicated subscript glyphs. When /// `{true}`, synthesized glyphs may still be used in case the font does not /// provide the necessary subscript glyphs. /// /// ```example /// N#sub(typographic: true)[1] /// N#sub(typographic: false)[1] /// ``` #[default(true)] pub typographic: bool, /// The baseline shift for synthesized subscripts. /// /// This only applies to synthesized subscripts. In other words, this has no /// effect if `typographic` is `{true}` and the font provides the necessary /// subscript glyphs. pub baseline: Smart, /// The font size for synthesized subscripts. /// /// This only applies to synthesized subscripts. In other words, this has no /// effect if `typographic` is `{true}` and the font provides the necessary /// subscript glyphs. pub size: Smart, /// The text to display in subscript. #[required] pub body: Content, } impl Show for Packed { #[typst_macros::time(name = "sub", span = self.span())] fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult { let outer_text_size = TextElem::size_in(styles); Ok(self .body .clone() .styled(TextElem::set_subperscript(Some(ScriptSettings { typographic: self.typographic(styles), shift: self.baseline(styles).map(|l| -l.to_em(outer_text_size)), size: self.size(styles).map(|t| t.0.to_em(outer_text_size)), kind: ScriptKind::Sub, })))) } } /// Renders text in superscript. /// /// The text is rendered smaller and its baseline is raised. /// /// # Example /// ```example /// 1#super[st] try! /// ``` #[elem(title = "Superscript", Show)] pub struct SuperElem { /// Whether to create artificial superscripts by shifting and scaling down /// regular glyphs. /// /// Ideally, superscripts glyphs should be provided by the font (using the /// `subs` OpenType feature). Otherwise, Typst is able to synthesize /// superscripts as explained above. /// /// When this is set to `{false}`, synthesized glyphs will be used /// regardless of whether the font provides dedicated superscript glyphs. /// When `{true}`, synthesized glyphs may still be used in case the font /// does not provide the necessary superscript glyphs. /// /// ```example /// N#super(typographic: true)[1] /// N#super(typographic: false)[1] /// ``` #[default(true)] pub typographic: bool, /// The baseline shift for synthesized superscripts. /// /// This only applies to synthesized superscripts. In other words, this has /// no effect if `typographic` is `{true}` and the font provides the /// necessary superscript glyphs. pub baseline: Smart, /// The font size for synthesized superscripts. /// /// This only applies to synthesized superscripts. In other words, this has /// no effect if `typographic` is `{true}` and the font provides the /// necessary superscript glyphs. pub size: Smart, /// The text to display in superscript. #[required] pub body: Content, } impl Show for Packed { #[typst_macros::time(name = "super", span = self.span())] fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult { let outer_text_size = TextElem::size_in(styles); Ok(self .body .clone() .styled(TextElem::set_subperscript(Some(ScriptSettings { typographic: self.typographic(styles), shift: self.baseline(styles).map(|l| -l.to_em(outer_text_size)), size: self.size(styles).map(|t| t.0.to_em(outer_text_size)), kind: ScriptKind::Super, })))) } } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ScriptKind { Sub, Super, } impl ScriptKind { pub const fn default_metrics(self) -> ScriptMetrics { match self { Self::Sub => ScriptMetrics::default_subscript(), Self::Super => ScriptMetrics::default_superscript(), } } pub const fn read_metrics(self, font_metrics: &FontMetrics) -> ScriptMetrics { match self { Self::Sub => font_metrics.subscript, Self::Super => font_metrics.superscript, } } /// The corresponding OpenType feature. pub const fn feature(self) -> Tag { match self { Self::Sub => Tag::from_bytes(b"subs"), Self::Super => Tag::from_bytes(b"sups"), } } } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct ScriptSettings { /// Whether the OpenType feature should be used if possible. pub typographic: bool, pub shift: Smart, pub size: Smart, pub kind: ScriptKind, }