#![cfg(feature = "pdf")]
use hyper_render::{render, render_to_pdf, Config, OutputFormat};
const PDF_SIGNATURE: &[u8] = b"%PDF-";
fn is_valid_pdf(data: &[u8]) -> bool {
if !data.starts_with(PDF_SIGNATURE) {
return false;
}
let tail = if data.len() > 1024 {
&data[data.len() - 1024..]
} else {
data
};
tail.windows(5).any(|w| w == b"%%EOF")
}
fn pdf_contains(data: &[u8], pattern: &[u8]) -> bool {
data.windows(pattern.len()).any(|w| w == pattern)
}
#[test]
fn test_pdf_basic_render() {
let html = "<html><body><h1>Hello</h1></body></html>";
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "render should succeed");
let bytes = result.unwrap();
assert!(!bytes.is_empty(), "output should not be empty");
assert!(
bytes.starts_with(PDF_SIGNATURE),
"output should start with PDF magic bytes"
);
assert!(is_valid_pdf(&bytes), "output should be valid PDF structure");
}
#[test]
fn test_pdf_render_to_pdf_convenience() {
let html = "<html><body><p>Test</p></body></html>";
let config = Config::new();
let result = render_to_pdf(html, config);
assert!(result.is_ok(), "render_to_pdf should succeed");
let bytes = result.unwrap();
assert!(is_valid_pdf(&bytes), "output should be valid PDF");
}
#[test]
fn test_pdf_contains_page_dimensions() {
let html = "<html><body></body></html>";
let config = Config::new()
.width(800)
.height(600)
.format(OutputFormat::Pdf);
let bytes = render(html, config).expect("render should succeed");
assert!(
pdf_contains(&bytes, b"MediaBox") || pdf_contains(&bytes, b"/MediaBox"),
"PDF should contain page dimensions"
);
}
#[test]
fn test_pdf_auto_height() {
let html = r#"
<html>
<body style="margin: 0;">
<div style="height: 1000px;"></div>
</body>
</html>
"#;
let config = Config::new()
.width(400)
.height(100)
.auto_height(true)
.format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "auto_height PDF should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_with_text_content() {
let html = r#"<html><body><p>Hello World</p></body></html>"#;
let config = Config::new().format(OutputFormat::Pdf);
let bytes = render(html, config).expect("render should succeed");
assert!(
pdf_contains(&bytes, b"/Font") || pdf_contains(&bytes, b"Font"),
"PDF with text should contain font references"
);
}
#[test]
fn test_pdf_background_colors() {
let html = r#"
<html>
<body style="background: #ff0000;">
<div style="background: blue; width: 100px; height: 100px;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "PDF with backgrounds should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_custom_page_background() {
let html = "<html><body></body></html>";
let config = Config::new()
.format(OutputFormat::Pdf)
.background([200, 200, 200, 255]);
let result = render(html, config);
assert!(result.is_ok(), "custom background should work");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_unicode_content() {
let html = r#"<html><body><p>Unicode: 日本語 䏿–‡ 한êµì–´</p></body></html>"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "unicode content should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_styled_content() {
let html = r#"
<html>
<body style="font-family: sans-serif; padding: 20px;">
<h1 style="color: navy;">Document Title</h1>
<p style="color: #333; line-height: 1.5;">
This is a styled paragraph with various CSS properties.
</p>
<ul>
<li>Item one</li>
<li>Item two</li>
</ul>
</body>
</html>
"#;
let config = Config::new()
.width(600)
.height(400)
.format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "styled content should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_nested_elements() {
let html = r#"
<html>
<body>
<div style="padding: 10px;">
<div style="padding: 10px;">
<div style="padding: 10px;">
<p>Deeply nested</p>
</div>
</div>
</div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "nested elements should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_flexbox_layout() {
let html = r#"
<html>
<body>
<div style="display: flex; gap: 10px;">
<div style="flex: 1; background: red;">A</div>
<div style="flex: 1; background: green;">B</div>
<div style="flex: 1; background: blue;">C</div>
</div>
</body>
</html>
"#;
let config = Config::new()
.width(400)
.height(200)
.format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "flexbox layout should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_minimum_dimensions() {
let html = "<html><body></body></html>";
let config = Config::new()
.width(Config::MIN_DIMENSION)
.height(Config::MIN_DIMENSION)
.format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "minimum dimensions should work");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_radius_uniform() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: red; border-radius: 10px;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "border-radius should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_radius_non_uniform() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: blue;
border-radius: 20px 10px 30px 5px;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "non-uniform border-radius should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_radius_percentage() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: green; border-radius: 50%;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "percentage border-radius should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_radius_with_text() {
let html = r#"
<html>
<body>
<div style="width: 200px; padding: 20px; background: #333;
color: white; border-radius: 16px;">
Text inside rounded box
</div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "border-radius with text should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_linear_gradient_horizontal() {
let html = r#"
<html>
<body>
<div style="width: 200px; height: 100px;
background: linear-gradient(to right, red, blue);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "horizontal gradient should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_linear_gradient_vertical() {
let html = r#"
<html>
<body>
<div style="width: 200px; height: 100px;
background: linear-gradient(to bottom, green, yellow);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "vertical gradient should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_linear_gradient_angled() {
let html = r#"
<html>
<body>
<div style="width: 200px; height: 100px;
background: linear-gradient(45deg, red, yellow, green);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "angled gradient should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_linear_gradient_corner() {
let html = r#"
<html>
<body>
<div style="width: 200px; height: 100px;
background: linear-gradient(to bottom right, purple, orange);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "corner gradient should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_linear_gradient_with_stops() {
let html = r#"
<html>
<body>
<div style="width: 200px; height: 100px;
background: linear-gradient(90deg, red 0%, yellow 25%, green 50%, blue 100%);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "gradient with stops should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_gradient_with_border_radius() {
let html = r#"
<html>
<body>
<div style="width: 200px; height: 100px; border-radius: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "gradient with border-radius should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_box_shadow_basic() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
box-shadow: 5px 5px 10px rgba(0,0,0,0.5);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "basic box-shadow should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_box_shadow_with_spread() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
box-shadow: 0 0 10px 5px rgba(0,0,255,0.3);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "box-shadow with spread should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_box_shadow_inset() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
box-shadow: inset 0 0 20px rgba(0,0,0,0.3);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "inset box-shadow should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_box_shadow_multiple() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
box-shadow: 5px 5px 10px rgba(0,0,0,0.3),
-5px -5px 10px rgba(255,255,255,0.5);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "multiple box-shadows should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_box_shadow_with_border_radius() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
border-radius: 16px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(
result.is_ok(),
"box-shadow with border-radius should render"
);
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_uniform() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
border: 5px solid red;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "uniform border should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_per_edge() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
border-top: 2px solid red;
border-right: 4px solid green;
border-bottom: 6px solid blue;
border-left: 8px solid yellow;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "per-edge borders should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_with_border_radius() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
border: 3px solid blue; border-radius: 16px;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "border with border-radius should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_transparent() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
border: 5px solid transparent;"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "transparent border should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}
#[test]
fn test_pdf_border_with_alpha() {
let html = r#"
<html>
<body>
<div style="width: 100px; height: 100px; background: white;
border: 5px solid rgba(255, 0, 0, 0.5);"></div>
</body>
</html>
"#;
let config = Config::new().format(OutputFormat::Pdf);
let result = render(html, config);
assert!(result.is_ok(), "border with alpha should render");
assert!(is_valid_pdf(&result.unwrap()), "output should be valid PDF");
}