Skip to main content

typst_html/
lib.rs

1//! Typst's HTML exporter.
2
3pub mod attr;
4pub mod property;
5pub mod tag;
6
7mod charsets;
8mod convert;
9mod css;
10mod document;
11mod dom;
12mod encode;
13mod fragment;
14mod introspect;
15mod link;
16mod mathml;
17mod rules;
18mod typed;
19
20pub use self::document::{html_document, html_document_for_bundle};
21pub use self::dom::*;
22pub use self::encode::{HtmlOptions, html, html_in_bundle};
23pub use self::introspect::HtmlIntrospector;
24pub use self::link::create_link_anchors;
25pub use self::rules::{html_mathml_body, html_span_filled, register};
26
27use ecow::EcoString;
28use typst_library::Category;
29use typst_library::foundations::{Content, Module, Scope};
30use typst_library::introspection::Location;
31use typst_macros::elem;
32
33/// Creates the module with all HTML definitions.
34pub fn module() -> Module {
35    let mut html = Scope::deduplicating();
36    html.start_category(Category::Html);
37    html.define_elem::<HtmlElem>();
38    html.define_elem::<FrameElem>();
39    crate::typed::define(&mut html);
40    Module::new("html", html)
41}
42
43/// An HTML element that can contain Typst content.
44///
45/// Typst's HTML export automatically generates the appropriate tags for most
46/// elements. However, sometimes, it is desirable to retain more control. For
47/// example, when using Typst to generate your blog, you could use this function
48/// to wrap each article in an `<article>` tag.
49///
50/// Typst is aware of what is valid HTML. A tag and its attributes must form
51/// syntactically valid HTML. Some tags, like `meta` do not accept content.
52/// Hence, you must not provide a body for them. We may add more checks in the
53/// future, so be sure that you are generating valid HTML when using this
54/// function.
55///
56/// Normally, Typst will generate `html`, `head`, and `body` tags for you. If
57/// you instead create them with this function, Typst will omit its own tags.
58///
59/// ```typ
60/// #html.elem("div", attrs: (style: "background: aqua"))[
61///   A div with _Typst content_ inside!
62/// ]
63/// ```
64#[elem(name = "elem")]
65pub struct HtmlElem {
66    /// The element's tag.
67    #[required]
68    pub tag: HtmlTag,
69
70    /// The element's HTML attributes.
71    #[fold]
72    pub attrs: HtmlAttrs,
73
74    /// The element's CSS properties. Currently only used for generated styles.
75    #[internal]
76    #[parse(Some(css::Properties::default()))]
77    pub css: css::Properties,
78
79    /// The contents of the HTML element.
80    ///
81    /// The body can be arbitrary Typst content.
82    #[positional]
83    pub body: Option<Content>,
84
85    /// The element's logical parent, if any.
86    #[internal]
87    #[synthesized]
88    pub parent: Location,
89
90    /// A role that should be applied to the top-level styled HTML element, but
91    /// not its descendants. If we ever get set rules that apply to a specific
92    /// element instead of a subtree, they could supplant this. If we need the
93    /// same mechanism for things like `class`, this could potentially also be
94    /// extended to arbitrary attributes. It's minimal for now.
95    ///
96    /// This is ignored for `<p>` elements as it otherwise tends to
97    /// unintentionally attach to paragraphs resulting from grouping of a single
98    /// element instead of attaching to that element. This is a bit of a hack,
99    /// but good enough for now as the `role` property is purely internal and we
100    /// control what it is used for.
101    #[internal]
102    #[ghost]
103    pub role: Option<EcoString>,
104}
105
106impl HtmlElem {
107    /// Add an attribute to the element.
108    pub fn with_attr(mut self, attr: HtmlAttr, value: impl Into<EcoString>) -> Self {
109        self.attrs
110            .as_option_mut()
111            .get_or_insert_with(Default::default)
112            .push(attr, value);
113        self
114    }
115
116    /// Adds the attribute to the element if value is not `None`.
117    pub fn with_optional_attr(
118        self,
119        attr: HtmlAttr,
120        value: Option<impl Into<EcoString>>,
121    ) -> Self {
122        if let Some(value) = value { self.with_attr(attr, value) } else { self }
123    }
124}
125
126/// An element that lays out its content as an inline SVG.
127///
128/// Sometimes, converting Typst content to HTML is not desirable. This can be
129/// the case for plots and other content that relies on positioning and styling
130/// to convey its message.
131///
132/// This function allows you to use the Typst layout engine that would also be
133/// used for PDF, SVG, and PNG export to render a part of your document exactly
134/// how it would appear when exported in one of these formats. It embeds the
135/// content as an inline SVG.
136#[elem]
137pub struct FrameElem {
138    /// The content that shall be laid out.
139    #[positional]
140    #[required]
141    pub body: Content,
142}