diff --git a/crates/typst-library/src/model/mod.rs b/crates/typst-library/src/model/mod.rs index 9bdbf0013..16ac5900d 100644 --- a/crates/typst-library/src/model/mod.rs +++ b/crates/typst-library/src/model/mod.rs @@ -20,6 +20,7 @@ mod reference; mod strong; mod table; mod terms; +mod title; pub use self::bibliography::*; pub use self::cite::*; @@ -39,6 +40,7 @@ pub use self::reference::*; pub use self::strong::*; pub use self::table::*; pub use self::terms::*; +pub use self::title::*; use crate::foundations::Scope; @@ -49,6 +51,7 @@ pub fn define(global: &mut Scope) { global.define_elem::(); global.define_elem::(); global.define_elem::(); + global.define_elem::(); global.define_elem::(); global.define_elem::(); global.define_elem::(); diff --git a/crates/typst-library/src/model/title.rs b/crates/typst-library/src/model/title.rs new file mode 100644 index 000000000..2dd42e19c --- /dev/null +++ b/crates/typst-library/src/model/title.rs @@ -0,0 +1,68 @@ +use crate::{ + diag::SourceResult, + engine::Engine, + foundations::{ + elem, Content, NativeElement, Packed, Show, ShowSet, Smart, StyleChain, Styles, + TargetElem, + }, + html::{tag, HtmlElem}, + introspection::Locatable, + layout::{AlignElem, Alignment, BlockBody, BlockElem, Em}, + text::{FontWeight, TextElem, TextSize}, +}; + +/// A document title. +/// +/// Should be used to display the main title of the whole document, and should +/// occur only once per document. +/// +/// Shows as `h1` in HTML. In contrast, a heading of level 1 +/// (created with `= Some Heading`) will show as `h2`. +/// +/// # Example +/// ```example +/// #title[Interstellar Mail Delivery] +/// +/// = Introduction +/// In recent years, ... +/// ``` +#[elem(Locatable, Show, ShowSet)] +pub struct TitleElem { + /// The content of the title. + #[required] + pub body: Content, +} + +impl Show for Packed { + #[typst_macros::time(name = "title", span = self.span())] + fn show(&self, _engine: &mut Engine, styles: StyleChain) -> SourceResult { + let html = TargetElem::target_in(styles).is_html(); + + let span = self.span(); + let realized = self.body().clone(); + + Ok(if html { + HtmlElem::new(tag::h1).with_body(Some(realized)).pack().spanned(span) + } else { + let realized = BlockBody::Content(realized); + BlockElem::new().with_body(Some(realized)).pack().spanned(span) + }) + } +} + +impl ShowSet for Packed { + fn show_set(&self, _styles: StyleChain) -> Styles { + const SIZE: Em = Em::new(1.6); + const ABOVE: Em = Em::new(1.125); + const BELOW: Em = Em::new(0.75); + + let mut out = Styles::new(); + out.set(TextElem::set_size(TextSize(SIZE.into()))); + out.set(TextElem::set_weight(FontWeight::BOLD)); + out.set(BlockElem::set_above(Smart::Custom(ABOVE.into()))); + out.set(BlockElem::set_below(Smart::Custom(BELOW.into()))); + out.set(BlockElem::set_sticky(true)); + out.set(AlignElem::set_alignment(Alignment::CENTER)); + out + } +} diff --git a/tests/ref/html/title-and-heading.html b/tests/ref/html/title-and-heading.html new file mode 100644 index 000000000..a6aa7ec0c --- /dev/null +++ b/tests/ref/html/title-and-heading.html @@ -0,0 +1,15 @@ + + + + + + + +

+ A cool title +

+

+ Some level one heading +

+ + \ No newline at end of file diff --git a/tests/ref/html/title-basic.html b/tests/ref/html/title-basic.html new file mode 100644 index 000000000..7b44c7f28 --- /dev/null +++ b/tests/ref/html/title-basic.html @@ -0,0 +1,12 @@ + + + + + + + +

+ Some Title +

+ + \ No newline at end of file diff --git a/tests/ref/title-and-heading.png b/tests/ref/title-and-heading.png new file mode 100644 index 000000000..3cf9654b5 Binary files /dev/null and b/tests/ref/title-and-heading.png differ diff --git a/tests/ref/title-basic.png b/tests/ref/title-basic.png new file mode 100644 index 000000000..df3af472c Binary files /dev/null and b/tests/ref/title-basic.png differ diff --git a/tests/ref/title-show-rule.png b/tests/ref/title-show-rule.png new file mode 100644 index 000000000..9b0c5296d Binary files /dev/null and b/tests/ref/title-show-rule.png differ diff --git a/tests/suite/model/title.typ b/tests/suite/model/title.typ new file mode 100644 index 000000000..cfc2d153f --- /dev/null +++ b/tests/suite/model/title.typ @@ -0,0 +1,12 @@ +// Test title element. + +--- title-basic render html --- +#title[Some Title] + +--- title-and-heading render html --- +#title([A cool title]) += Some level one heading + +--- title-show-rule --- +#show title: set text(3em) +#title[Some Title]