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