Skip to main content

typst_library/foundations/
target.rs

1use std::any::Any;
2
3use comemo::Tracked;
4
5use crate::diag::{HintedStrResult, SourceResult};
6use crate::engine::Engine;
7use crate::foundations::{Cast, Content, Context, StyleChain, elem, func};
8use crate::introspection::Introspector;
9
10/// A compilation output for a particular target.
11///
12/// Has a 1-1 relationship with the variants of [`Target`].
13pub trait Output: Any {
14    /// The target associated with the output.
15    fn target() -> Target
16    where
17        Self: Sized;
18
19    /// Creates the output.
20    fn create(
21        engine: &mut Engine,
22        content: &Content,
23        styles: StyleChain,
24    ) -> SourceResult<Self>
25    where
26        Self: Sized;
27
28    /// Get the output's introspector.
29    fn introspector(&self) -> &dyn Introspector;
30}
31
32/// A trait for accepting an arbitrary kind of output as an argument.
33///
34/// Can be used to accept a reference to
35/// - any kind of sized type that implements [`Output`], or
36/// - the trait object [`&dyn Output`].
37///
38/// Should be used as `impl AsOutput` rather than `&impl AsOutput`.
39///
40/// # Why is this needed?
41/// Unfortunately, `&impl Output` can't be turned into `&dyn Output` in a
42/// generic function. Directly accepting `&dyn Output` is of course also
43/// possible, but is less convenient, especially in cases where the document is
44/// optional.
45///
46/// See also
47/// <https://users.rust-lang.org/t/converting-from-generic-unsized-parameter-to-trait-object/72376>
48pub trait AsOutput {
49    /// Turns the reference into the trait object.
50    fn as_output(&self) -> &dyn Output;
51}
52
53impl AsOutput for &dyn Output {
54    fn as_output(&self) -> &dyn Output {
55        *self
56    }
57}
58
59impl<T: Output> AsOutput for &T {
60    fn as_output(&self) -> &dyn Output {
61        *self
62    }
63}
64
65/// The compilation target.
66#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
67pub enum Target {
68    /// The target that is used for paged, fully laid-out content.
69    #[default]
70    Paged,
71    /// The target that is used for HTML export.
72    Html,
73    /// The target for _bundle_ export. This export target can produce multiple
74    /// [documents]($document) and [assets]($asset) from a single Typst project.
75    Bundle,
76}
77
78impl Target {
79    /// Whether this is the HTML target.
80    pub fn is_html(self) -> bool {
81        self == Self::Html
82    }
83}
84
85/// This element exists solely to host the `target` style chain field. It is
86/// never constructed and not visible to users.
87#[elem]
88pub struct TargetElem {
89    /// The compilation target.
90    pub target: Target,
91}
92
93/// Returns the current export target.
94///
95/// This function returns
96/// - `{"paged"}` in @pdf[PDF], @reference:png[PNG], and
97///   @reference:svg[SVG] export, or within an @html.frame[HTML frame]
98/// - `{"html"}` in @html[HTML] export
99/// - `{"bundle"}` in @reference:bundle[Bundle] export
100///
101/// = When to use it <when-to-use-it>
102/// This function allows you to format your document properly across the paged,
103/// HTML, and multi file export targets. It should primarily be used in
104/// templates and show rules, rather than directly in content. This way, the
105/// document's contents can be fully agnostic to the export target and content
106/// can be shared between different export targets.
107///
108/// = Varying targets <varying-targets>
109/// This function is @reference:context[contextual] as the target can vary
110/// within a single compilation: When exporting to HTML, the target will be
111/// `{"paged"}` while within an @html.frame.
112///
113/// = Example <example>
114/// ```example
115/// #let kbd(it) = context {
116///   if target() == "html" {
117///     html.elem("kbd", it)
118///   } else {
119///     set text(fill: rgb("#1f2328"))
120///     let r = 3pt
121///     box(
122///       fill: rgb("#f6f8fa"),
123///       stroke: rgb("#d1d9e0b3"),
124///       outset: (y: r),
125///       inset: (x: r),
126///       radius: r,
127///       raw(it)
128///     )
129///   }
130/// }
131///
132/// Press #kbd("F1") for help.
133/// ```
134#[func(contextual)]
135pub fn target(context: Tracked<Context>) -> HintedStrResult<Target> {
136    Ok(context.styles()?.get(TargetElem::target))
137}