Skip to main content

typst_batch/html/
frame.rs

1//! HTML frame wrapper.
2
3#[cfg(feature = "svg")]
4use super::HtmlDocument;
5
6/// A frame that should be rendered as SVG.
7///
8/// Frames contain typst-rendered content (math, images, plots, etc.)
9/// that needs to be embedded as inline SVG in HTML output.
10#[derive(Debug, Clone, Copy)]
11pub struct HtmlFrame<'a>(pub(crate) &'a typst_html::HtmlFrame);
12
13impl<'a> HtmlFrame<'a> {
14    /// Get the frame's ID, if any.
15    #[inline]
16    pub fn id(&self) -> Option<&str> {
17        self.0.id.as_deref()
18    }
19
20    // =========================================================================
21    // Measurement API - for inline SVG vertical alignment
22    // =========================================================================
23
24    /// Get the frame's size in points (width, height).
25    ///
26    /// Useful for calculating CSS dimensions or vertical alignment.
27    #[inline]
28    pub fn size(&self) -> (f64, f64) {
29        let s = self.0.inner.size();
30        (s.x.to_pt(), s.y.to_pt())
31    }
32
33    /// Get the frame's width in points.
34    #[inline]
35    pub fn width(&self) -> f64 {
36        self.0.inner.width().to_pt()
37    }
38
39    /// Get the frame's height in points.
40    #[inline]
41    pub fn height(&self) -> f64 {
42        self.0.inner.height().to_pt()
43    }
44
45    /// Get the frame's baseline offset from top in points.
46    ///
47    /// For inline math, you can use this to calculate `vertical-align`:
48    /// ```ignore
49    /// let shift = frame.height() - frame.baseline();
50    /// let css = format!("vertical-align: -{}pt", shift);
51    /// ```
52    #[inline]
53    pub fn baseline(&self) -> f64 {
54        self.0.inner.baseline().to_pt()
55    }
56
57    /// Get the text size (in points) where the frame was defined.
58    ///
59    /// Useful for converting pt to em units:
60    /// ```ignore
61    /// let shift_em = shift_pt / frame.text_size();
62    /// ```
63    #[inline]
64    pub fn text_size(&self) -> f64 {
65        self.0.text_size.to_pt()
66    }
67
68    /// Calculate the vertical-align offset in em units for inline display.
69    ///
70    /// Returns a negative value suitable for CSS `vertical-align`.
71    /// For baseline-aligned inline content like math formulas.
72    #[inline]
73    pub fn vertical_align_em(&self) -> f64 {
74        let shift = self.height() - self.baseline();
75        -shift / self.text_size()
76    }
77
78    // =========================================================================
79    // Rendering
80    // =========================================================================
81
82    /// Render this frame to an SVG string.
83    ///
84    /// The returned string is a complete `<svg>...</svg>` element
85    /// suitable for embedding in HTML.
86    ///
87    /// # Example
88    ///
89    /// ```ignore
90    /// let svg = frame.to_svg(&doc);
91    /// // svg contains: <svg class="typst-frame" style="..." viewBox="...">...</svg>
92    /// ```
93    ///
94    /// # Note
95    ///
96    /// This method requires the `svg` feature to be enabled.
97    #[cfg(feature = "svg")]
98    pub fn to_svg(&self, doc: &HtmlDocument) -> String {
99        doc.render_frame_svg(self)
100    }
101
102    /// Render this frame to an inline SVG string with vertical-align style.
103    ///
104    /// Convenient wrapper that applies the calculated `vertical-align` offset.
105    ///
106    /// # Example
107    ///
108    /// ```ignore
109    /// let html = frame.to_inline_svg(&doc);
110    /// // Returns: <span style="vertical-align: -0.5em"><svg ...></svg></span>
111    /// ```
112    #[cfg(feature = "svg")]
113    pub fn to_inline_svg(&self, doc: &HtmlDocument) -> String {
114        let svg = self.to_svg(doc);
115        let align = self.vertical_align_em();
116        format!(
117            r#"<span style="vertical-align: {:.4}em">{}</span>"#,
118            align, svg
119        )
120    }
121}
122