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}