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}