diff options
Diffstat (limited to 'crates/typst-library/src/text/misc.rs')
| -rw-r--r-- | crates/typst-library/src/text/misc.rs | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/crates/typst-library/src/text/misc.rs b/crates/typst-library/src/text/misc.rs new file mode 100644 index 00000000..811b027e --- /dev/null +++ b/crates/typst-library/src/text/misc.rs @@ -0,0 +1,330 @@ +use super::TextElem; +use crate::prelude::*; + +/// A text space. +/// +/// Display: Space +/// Category: text +#[element(Behave, Unlabellable, PlainText)] +pub struct SpaceElem {} + +impl Behave for SpaceElem { + fn behaviour(&self) -> Behaviour { + Behaviour::Weak(2) + } +} + +impl Unlabellable for SpaceElem {} + +impl PlainText for SpaceElem { + fn plain_text(&self, text: &mut EcoString) { + text.push(' '); + } +} + +/// Inserts a line break. +/// +/// Advances the paragraph to the next line. A single trailing line break at the +/// end of a paragraph is ignored, but more than one creates additional empty +/// lines. +/// +/// ## Example { #example } +/// ```example +/// *Date:* 26.12.2022 \ +/// *Topic:* Infrastructure Test \ +/// *Severity:* High \ +/// ``` +/// +/// ## Syntax { #syntax } +/// This function also has dedicated syntax: To insert a line break, simply write +/// a backslash followed by whitespace. This always creates an unjustified +/// break. +/// +/// Display: Line Break +/// Category: text +#[element(Behave)] +pub struct LinebreakElem { + /// Whether to justify the line before the break. + /// + /// This is useful if you found a better line break opportunity in your + /// justified text than Typst did. + /// + /// ```example + /// #set par(justify: true) + /// #let jb = linebreak(justify: true) + /// + /// I have manually tuned the #jb + /// line breaks in this paragraph #jb + /// for an _interesting_ result. #jb + /// ``` + #[default(false)] + pub justify: bool, +} + +impl Behave for LinebreakElem { + fn behaviour(&self) -> Behaviour { + Behaviour::Destructive + } +} + +/// Strongly emphasizes content by increasing the font weight. +/// +/// Increases the current font weight by a given `delta`. +/// +/// ## Example { #example } +/// ```example +/// This is *strong.* \ +/// This is #strong[too.] \ +/// +/// #show strong: set text(red) +/// And this is *evermore.* +/// ``` +/// +/// ## Syntax { #syntax } +/// This function also has dedicated syntax: To strongly emphasize content, +/// simply enclose it in stars/asterisks (`*`). Note that this only works at +/// word boundaries. To strongly emphasize part of a word, you have to use the +/// function. +/// +/// Display: Strong Emphasis +/// Category: text +#[element(Show)] +pub struct StrongElem { + /// The delta to apply on the font weight. + /// + /// ```example + /// #set strong(delta: 0) + /// No *effect!* + /// ``` + #[default(300)] + pub delta: i64, + + /// The content to strongly emphasize. + #[required] + pub body: Content, +} + +impl Show for StrongElem { + #[tracing::instrument(name = "StrongElem::show", skip_all)] + fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { + Ok(self.body().styled(TextElem::set_delta(Delta(self.delta(styles))))) + } +} + +/// A delta that is summed up when folded. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Delta(pub i64); + +cast! { + Delta, + self => self.0.into_value(), + v: i64 => Self(v), +} + +impl Fold for Delta { + type Output = i64; + + fn fold(self, outer: Self::Output) -> Self::Output { + outer + self.0 + } +} + +/// Emphasizes content by setting it in italics. +/// +/// - If the current [text style]($func/text.style) is `{"normal"}`, +/// this turns it into `{"italic"}`. +/// - If it is already `{"italic"}` or `{"oblique"}`, +/// it turns it back to `{"normal"}`. +/// +/// ## Example { #example } +/// ```example +/// This is _emphasized._ \ +/// This is #emph[too.] +/// +/// #show emph: it => { +/// text(blue, it.body) +/// } +/// +/// This is _emphasized_ differently. +/// ``` +/// +/// ## Syntax { #syntax } +/// This function also has dedicated syntax: To emphasize content, simply +/// enclose it in underscores (`_`). Note that this only works at word +/// boundaries. To emphasize part of a word, you have to use the function. +/// +/// Display: Emphasis +/// Category: text +#[element(Show)] +pub struct EmphElem { + /// The content to emphasize. + #[required] + pub body: Content, +} + +impl Show for EmphElem { + #[tracing::instrument(name = "EmphElem::show", skip(self))] + fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> { + Ok(self.body().styled(TextElem::set_emph(Toggle))) + } +} + +/// A toggle that turns on and off alternatingly if folded. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Toggle; + +cast! { + Toggle, + self => Value::None, + _: Value => Self, +} + +impl Fold for Toggle { + type Output = bool; + + fn fold(self, outer: Self::Output) -> Self::Output { + !outer + } +} + +/// Converts text or content to lowercase. +/// +/// ## Example { #example } +/// ```example +/// #lower("ABC") \ +/// #lower[*My Text*] \ +/// #lower[already low] +/// ``` +/// +/// Display: Lowercase +/// Category: text +#[func] +pub fn lower( + /// The text to convert to lowercase. + text: Caseable, +) -> Caseable { + case(text, Case::Lower) +} + +/// Converts text or content to uppercase. +/// +/// ## Example { #example } +/// ```example +/// #upper("abc") \ +/// #upper[*my text*] \ +/// #upper[ALREADY HIGH] +/// ``` +/// +/// Display: Uppercase +/// Category: text +#[func] +pub fn upper( + /// The text to convert to uppercase. + text: Caseable, +) -> Caseable { + case(text, Case::Upper) +} + +/// Change the case of text. +fn case(text: Caseable, case: Case) -> Caseable { + match text { + Caseable::Str(v) => Caseable::Str(case.apply(&v).into()), + Caseable::Content(v) => { + Caseable::Content(v.styled(TextElem::set_case(Some(case)))) + } + } +} + +/// A value whose case can be changed. +pub enum Caseable { + Str(Str), + Content(Content), +} + +cast! { + Caseable, + self => match self { + Self::Str(v) => v.into_value(), + Self::Content(v) => v.into_value(), + }, + v: Str => Self::Str(v), + v: Content => Self::Content(v), +} + +/// A case transformation on text. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)] +pub enum Case { + /// Everything is lowercased. + Lower, + /// Everything is uppercased. + Upper, +} + +impl Case { + /// Apply the case to a string. + pub fn apply(self, text: &str) -> String { + match self { + Self::Lower => text.to_lowercase(), + Self::Upper => text.to_uppercase(), + } + } +} + +/// Displays text in small capitals. +/// +/// _Note:_ This enables the OpenType `smcp` feature for the font. Not all fonts +/// support this feature. Sometimes smallcaps are part of a dedicated font and +/// sometimes they are not available at all. In the future, this function will +/// support selecting a dedicated smallcaps font as well as synthesizing +/// smallcaps from normal letters, but this is not yet implemented. +/// +/// ## Example { #example } +/// ```example +/// #set par(justify: true) +/// #set heading(numbering: "I.") +/// +/// #show heading: it => { +/// set block(below: 10pt) +/// set text(weight: "regular") +/// align(center, smallcaps(it)) +/// } +/// +/// = Introduction +/// #lorem(40) +/// ``` +/// +/// Display: Small Capitals +/// Category: text +#[func] +pub fn smallcaps( + /// The text to display to small capitals. + body: Content, +) -> Content { + body.styled(TextElem::set_smallcaps(true)) +} + +/// Creates blind text. +/// +/// This function yields a Latin-like _Lorem Ipsum_ blind text with the given +/// number of words. The sequence of words generated by the function is always +/// the same but randomly chosen. As usual for blind texts, it does not make any +/// sense. Use it as a placeholder to try layouts. +/// +/// ## Example { #example } +/// ```example +/// = Blind Text +/// #lorem(30) +/// +/// = More Blind Text +/// #lorem(15) +/// ``` +/// +/// Display: Blind Text +/// Category: text +#[func] +pub fn lorem( + /// The length of the blind text in words. + words: usize, +) -> Str { + lipsum::lipsum(words).replace("--", "–").into() +} |
