hyper_render/config.rs
1//! Configuration types for rendering.
2
3use crate::error::{Error, Result};
4
5/// Output format for rendered content.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum OutputFormat {
8 /// PNG image format (raster).
9 #[default]
10 Png,
11 /// PDF document format (vector).
12 Pdf,
13}
14
15impl std::fmt::Display for OutputFormat {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 match self {
18 OutputFormat::Png => write!(f, "png"),
19 OutputFormat::Pdf => write!(f, "pdf"),
20 }
21 }
22}
23
24/// Color scheme preference for rendering.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
26pub enum ColorScheme {
27 /// Light color scheme.
28 #[default]
29 Light,
30 /// Dark color scheme.
31 Dark,
32}
33
34impl From<ColorScheme> for blitz_traits::shell::ColorScheme {
35 fn from(scheme: ColorScheme) -> Self {
36 match scheme {
37 ColorScheme::Light => blitz_traits::shell::ColorScheme::Light,
38 ColorScheme::Dark => blitz_traits::shell::ColorScheme::Dark,
39 }
40 }
41}
42
43/// Configuration for HTML rendering.
44///
45/// Use the builder pattern to construct a configuration:
46///
47/// ```rust
48/// use hyper_render::{Config, OutputFormat, ColorScheme};
49///
50/// let config = Config::new()
51/// .width(1200)
52/// .height(800)
53/// .scale(2.0)
54/// .format(OutputFormat::Pdf)
55/// .color_scheme(ColorScheme::Dark);
56/// ```
57#[derive(Debug, Clone)]
58pub struct Config {
59 /// Width of the viewport in pixels.
60 pub width: u32,
61
62 /// Height of the viewport in pixels.
63 ///
64 /// For PDF output, this may be adjusted based on content length
65 /// if `auto_height` is enabled.
66 pub height: u32,
67
68 /// Scale factor for rendering (e.g., 2.0 for retina displays).
69 pub scale: f32,
70
71 /// Output format (PNG or PDF).
72 pub format: OutputFormat,
73
74 /// Color scheme preference (light or dark mode).
75 pub color_scheme: ColorScheme,
76
77 /// Whether to automatically adjust height based on content.
78 ///
79 /// When enabled, the renderer will compute the actual content height
80 /// and use that instead of the configured height.
81 pub auto_height: bool,
82
83 /// Background color as RGBA (default: white).
84 pub background: [u8; 4],
85}
86
87impl Default for Config {
88 fn default() -> Self {
89 Self {
90 width: 800,
91 height: 600,
92 scale: 1.0,
93 format: OutputFormat::Png,
94 color_scheme: ColorScheme::Light,
95 auto_height: false,
96 background: [255, 255, 255, 255], // White
97 }
98 }
99}
100
101impl Config {
102 /// Create a new configuration with default values.
103 ///
104 /// Defaults:
105 /// - Width: 800px
106 /// - Height: 600px
107 /// - Scale: 1.0
108 /// - Format: PNG
109 /// - Color scheme: Light
110 pub fn new() -> Self {
111 Self::default()
112 }
113
114 /// Set the viewport width in pixels.
115 ///
116 /// # Example
117 ///
118 /// ```rust
119 /// use hyper_render::Config;
120 ///
121 /// let config = Config::new().width(1920);
122 /// assert_eq!(config.width, 1920);
123 /// ```
124 pub fn width(mut self, width: u32) -> Self {
125 self.width = width;
126 self
127 }
128
129 /// Set the viewport height in pixels.
130 ///
131 /// # Example
132 ///
133 /// ```rust
134 /// use hyper_render::Config;
135 ///
136 /// let config = Config::new().height(1080);
137 /// assert_eq!(config.height, 1080);
138 /// ```
139 pub fn height(mut self, height: u32) -> Self {
140 self.height = height;
141 self
142 }
143
144 /// Set both width and height at once.
145 ///
146 /// # Example
147 ///
148 /// ```rust
149 /// use hyper_render::Config;
150 ///
151 /// let config = Config::new().size(1920, 1080);
152 /// assert_eq!(config.width, 1920);
153 /// assert_eq!(config.height, 1080);
154 /// ```
155 pub fn size(mut self, width: u32, height: u32) -> Self {
156 self.width = width;
157 self.height = height;
158 self
159 }
160
161 /// Set the scale factor for rendering.
162 ///
163 /// Use 2.0 for retina/HiDPI displays to get crisp output.
164 ///
165 /// # Example
166 ///
167 /// ```rust
168 /// use hyper_render::Config;
169 ///
170 /// let config = Config::new().scale(2.0);
171 /// assert_eq!(config.scale, 2.0);
172 /// ```
173 pub fn scale(mut self, scale: f32) -> Self {
174 self.scale = scale;
175 self
176 }
177
178 /// Set the output format.
179 ///
180 /// # Example
181 ///
182 /// ```rust
183 /// use hyper_render::{Config, OutputFormat};
184 ///
185 /// let config = Config::new().format(OutputFormat::Pdf);
186 /// ```
187 pub fn format(mut self, format: OutputFormat) -> Self {
188 self.format = format;
189 self
190 }
191
192 /// Set the color scheme preference.
193 ///
194 /// This affects CSS media queries like `prefers-color-scheme`.
195 ///
196 /// # Example
197 ///
198 /// ```rust
199 /// use hyper_render::{Config, ColorScheme};
200 ///
201 /// let config = Config::new().color_scheme(ColorScheme::Dark);
202 /// ```
203 pub fn color_scheme(mut self, scheme: ColorScheme) -> Self {
204 self.color_scheme = scheme;
205 self
206 }
207
208 /// Enable automatic height detection.
209 ///
210 /// When enabled, the renderer will compute the actual content height
211 /// and use that instead of the configured height. This is useful for
212 /// rendering full-page content.
213 ///
214 /// # Example
215 ///
216 /// ```rust
217 /// use hyper_render::Config;
218 ///
219 /// let config = Config::new().auto_height(true);
220 /// ```
221 pub fn auto_height(mut self, auto: bool) -> Self {
222 self.auto_height = auto;
223 self
224 }
225
226 /// Set the background color as RGBA values.
227 ///
228 /// # Example
229 ///
230 /// ```rust
231 /// use hyper_render::Config;
232 ///
233 /// // Transparent background
234 /// let config = Config::new().background([0, 0, 0, 0]);
235 ///
236 /// // Light gray background
237 /// let config = Config::new().background([240, 240, 240, 255]);
238 /// ```
239 pub fn background(mut self, rgba: [u8; 4]) -> Self {
240 self.background = rgba;
241 self
242 }
243
244 /// Set a transparent background.
245 ///
246 /// Shorthand for `.background([0, 0, 0, 0])`.
247 pub fn transparent(self) -> Self {
248 self.background([0, 0, 0, 0])
249 }
250
251 /// Minimum supported width/height in pixels.
252 ///
253 /// Very small dimensions can cause overflow issues in the underlying
254 /// rendering engine.
255 pub const MIN_DIMENSION: u32 = 16;
256
257 /// Validate the configuration.
258 ///
259 /// Returns an error if any configuration values are invalid:
260 /// - Width must be at least 16
261 /// - Height must be at least 16
262 /// - Scale must be greater than 0
263 ///
264 /// This is called automatically by the render functions.
265 ///
266 /// # Example
267 ///
268 /// ```rust
269 /// use hyper_render::Config;
270 ///
271 /// let config = Config::new().width(0);
272 /// assert!(config.validate().is_err());
273 /// ```
274 pub fn validate(&self) -> Result<()> {
275 if self.width < Self::MIN_DIMENSION {
276 return Err(Error::InvalidConfig(format!(
277 "width must be at least {} pixels",
278 Self::MIN_DIMENSION
279 )));
280 }
281 if self.height < Self::MIN_DIMENSION {
282 return Err(Error::InvalidConfig(format!(
283 "height must be at least {} pixels",
284 Self::MIN_DIMENSION
285 )));
286 }
287 if self.scale <= 0.0 {
288 return Err(Error::InvalidConfig(
289 "scale must be greater than 0".to_string(),
290 ));
291 }
292 if !self.scale.is_finite() {
293 return Err(Error::InvalidConfig(
294 "scale must be a finite number".to_string(),
295 ));
296 }
297 Ok(())
298 }
299}