Skip to main content

hyper_render/
lib.rs

1//! # hyper-render
2//!
3//! A Chromium-free HTML rendering engine for generating PNG and PDF outputs.
4//!
5//! This library provides a simple, high-level API for rendering HTML content to
6//! images (PNG) or documents (PDF) without requiring a browser or Chromium dependency.
7//! It leverages the [Blitz](https://github.com/DioxusLabs/blitz) rendering engine
8//! for HTML/CSS parsing and layout.
9//!
10//! ## Features
11//!
12//! - **PNG output**: Render HTML to PNG images using CPU-based rendering
13//! - **PDF output**: Render HTML to PDF documents with vector graphics
14//! - **No browser required**: Pure Rust implementation, no Chromium/WebKit
15//! - **CSS support**: Flexbox, Grid, and common CSS properties via Stylo
16//!
17//! ## Quick Start
18//!
19//! ```rust,no_run
20//! use hyper_render::{render, Config, OutputFormat};
21//!
22//! let html = r#"
23//!     <html>
24//!         <body style="font-family: sans-serif; padding: 20px;">
25//!             <h1 style="color: navy;">Hello, World!</h1>
26//!             <p>Rendered without Chromium.</p>
27//!         </body>
28//!     </html>
29//! "#;
30//!
31//! // Render to PNG
32//! let png_bytes = render(html, Config::default())?;
33//! std::fs::write("output.png", png_bytes)?;
34//!
35//! // Render to PDF
36//! let pdf_bytes = render(html, Config::default().format(OutputFormat::Pdf))?;
37//! std::fs::write("output.pdf", pdf_bytes)?;
38//! # Ok::<(), hyper_render::Error>(())
39//! ```
40//!
41//! ## Configuration
42//!
43//! Use [`Config`] to customize the rendering:
44//!
45//! ```rust,no_run
46//! use hyper_render::{Config, OutputFormat};
47//!
48//! let config = Config::new()
49//!     .width(1200)
50//!     .height(800)
51//!     .scale(2.0)  // 2x resolution for retina displays
52//!     .format(OutputFormat::Png);
53//! ```
54
55mod config;
56mod error;
57mod render;
58
59pub use config::{ColorScheme, Config, OutputFormat};
60pub use error::{Error, Result};
61
62use blitz_dom::DocumentConfig;
63use blitz_html::HtmlDocument;
64use blitz_traits::shell::Viewport;
65
66/// Render HTML content to the specified output format.
67///
68/// This is the main entry point for rendering HTML. It parses the HTML,
69/// computes styles and layout, and renders to the format specified in the config.
70///
71/// # Arguments
72///
73/// * `html` - The HTML content to render
74/// * `config` - Rendering configuration (dimensions, format, scale, etc.)
75///
76/// # Returns
77///
78/// Returns the rendered output as bytes (PNG image data or PDF document).
79///
80/// # Errors
81///
82/// Returns an error if:
83/// - Configuration is invalid (zero dimensions, non-positive scale)
84/// - HTML parsing fails
85/// - Layout computation fails
86/// - Rendering fails
87/// - The requested output format feature is not enabled
88///
89/// # Example
90///
91/// ```rust,no_run
92/// use hyper_render::{render, Config, OutputFormat};
93///
94/// let html = "<h1>Hello</h1>";
95///
96/// // PNG output (default)
97/// let png = render(html, Config::default())?;
98///
99/// // PDF output
100/// let pdf = render(html, Config::default().format(OutputFormat::Pdf))?;
101/// # Ok::<(), hyper_render::Error>(())
102/// ```
103pub fn render(html: &str, config: Config) -> Result<Vec<u8>> {
104    // Validate configuration
105    config.validate()?;
106
107    // Parse HTML and create document
108    let mut document = create_document(html, &config)?;
109
110    // Resolve styles and compute layout
111    document.resolve(0.0);
112
113    // Render to the specified format
114    match config.format {
115        OutputFormat::Png => render::png::render_to_png(&document, &config),
116        OutputFormat::Pdf => render::pdf::render_to_pdf(&document, &config),
117    }
118}
119
120/// Render HTML content to PNG format.
121///
122/// Convenience function that renders directly to PNG without needing to specify
123/// the format in the config.
124///
125/// # Example
126///
127/// ```rust,no_run
128/// use hyper_render::{render_to_png, Config};
129///
130/// let png_bytes = render_to_png("<h1>Hello</h1>", Config::default())?;
131/// std::fs::write("output.png", png_bytes)?;
132/// # Ok::<(), hyper_render::Error>(())
133/// ```
134#[cfg(feature = "png")]
135pub fn render_to_png(html: &str, config: Config) -> Result<Vec<u8>> {
136    render(html, config.format(OutputFormat::Png))
137}
138
139/// Render HTML content to PDF format.
140///
141/// Convenience function that renders directly to PDF without needing to specify
142/// the format in the config.
143///
144/// # Example
145///
146/// ```rust,no_run
147/// use hyper_render::{render_to_pdf, Config};
148///
149/// let pdf_bytes = render_to_pdf("<h1>Hello</h1>", Config::default())?;
150/// std::fs::write("output.pdf", pdf_bytes)?;
151/// # Ok::<(), hyper_render::Error>(())
152/// ```
153#[cfg(feature = "pdf")]
154pub fn render_to_pdf(html: &str, config: Config) -> Result<Vec<u8>> {
155    render(html, config.format(OutputFormat::Pdf))
156}
157
158/// Create and configure a Blitz document from HTML.
159fn create_document(html: &str, config: &Config) -> Result<HtmlDocument> {
160    let viewport = Viewport::new(
161        config.width,
162        config.height,
163        config.scale,
164        config.color_scheme.into(),
165    );
166
167    let doc_config = DocumentConfig {
168        viewport: Some(viewport),
169        ..Default::default()
170    };
171
172    Ok(HtmlDocument::from_html(html, doc_config))
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178
179    #[test]
180    fn test_config_builder() {
181        let config = Config::new()
182            .width(1920)
183            .height(1080)
184            .scale(2.0)
185            .format(OutputFormat::Png);
186
187        assert_eq!(config.width, 1920);
188        assert_eq!(config.height, 1080);
189        assert_eq!(config.scale, 2.0);
190    }
191
192    #[test]
193    fn test_default_config() {
194        let config = Config::default();
195        assert_eq!(config.width, 800);
196        assert_eq!(config.height, 600);
197        assert_eq!(config.scale, 1.0);
198    }
199
200    #[test]
201    fn test_config_validation_zero_width() {
202        let config = Config::new().width(0);
203        assert!(config.validate().is_err());
204    }
205
206    #[test]
207    fn test_config_validation_zero_height() {
208        let config = Config::new().height(0);
209        assert!(config.validate().is_err());
210    }
211
212    #[test]
213    fn test_config_validation_zero_scale() {
214        let config = Config::new().scale(0.0);
215        assert!(config.validate().is_err());
216    }
217
218    #[test]
219    fn test_config_validation_negative_scale() {
220        let config = Config::new().scale(-1.0);
221        assert!(config.validate().is_err());
222    }
223
224    #[test]
225    fn test_config_validation_valid() {
226        let config = Config::new()
227            .width(Config::MIN_DIMENSION)
228            .height(Config::MIN_DIMENSION)
229            .scale(0.1);
230        assert!(config.validate().is_ok());
231    }
232}