Skip to main content

typst_library/model/
title.rs

1use crate::diag::{Hint, HintedStrResult};
2use crate::foundations::{Content, Packed, ShowSet, Smart, StyleChain, Styles, elem};
3use crate::introspection::{Locatable, Tagged};
4use crate::layout::{BlockElem, Em};
5use crate::model::DocumentElem;
6use crate::text::{FontWeight, TextElem, TextSize};
7
8/// A document title.
9///
10/// This should be used to display the main title of the whole document and
11/// should occur only once per document. In contrast, level 1 @heading[headings]
12/// are intended to be used for the top-level sections of the document.
13///
14/// Note that additional frontmatter (like an author list) that should appear
15/// together with the title does not belong in its body.
16///
17/// In HTML export, this shows as a `h1` element while level 1 headings show as
18/// `h2` elements.
19///
20/// = Example <example>
21/// ```example
22/// #set document(
23///   title: [Interstellar Mail Delivery]
24/// )
25///
26/// #title()
27///
28/// = Introduction
29/// In recent years, ...
30/// ```
31#[elem(Locatable, Tagged, ShowSet)]
32pub struct TitleElem {
33    /// The content of the title.
34    ///
35    /// When omitted (or `{auto}`), this will default to @document.title. In
36    /// this case, a document title must have been previously set with
37    /// `{set document(title: [..])}`.
38    ///
39    /// ```example
40    /// #set document(title: [Course ABC, Homework 1])
41    /// #title[Homework 1]
42    ///
43    /// ...
44    /// ```
45    #[positional]
46    pub body: Smart<Content>,
47}
48
49impl TitleElem {
50    pub fn resolve_body(&self, styles: StyleChain) -> HintedStrResult<Content> {
51        match self.body.get_cloned(styles) {
52            Smart::Auto => styles
53                .get_cloned(DocumentElem::title)
54                .ok_or("document title was not set")
55                .hint("set the title with `set document(title: [...])`")
56                .hint("or provide an explicit body with `title[..]`"),
57            Smart::Custom(body) => Ok(body),
58        }
59    }
60}
61
62impl ShowSet for Packed<TitleElem> {
63    fn show_set(&self, _styles: StyleChain) -> Styles {
64        const SIZE: Em = Em::new(1.7);
65        const ABOVE: Em = Em::new(1.125);
66        const BELOW: Em = Em::new(0.75);
67
68        let mut out = Styles::new();
69        out.set(TextElem::size, TextSize(SIZE.into()));
70        out.set(TextElem::weight, FontWeight::BOLD);
71        out.set(BlockElem::above, Smart::Custom(ABOVE.into()));
72        out.set(BlockElem::below, Smart::Custom(BELOW.into()));
73        out.set(BlockElem::sticky, true);
74        out
75    }
76}