extern crate printpdf;
use printpdf::*;
use std::collections::BTreeMap;
fn main() {
test_empty_block_collapse_through();
test_parent_child_top_escape();
test_parent_child_bottom_escape();
test_complex_nested_collapse();
test_multiple_empty_blocks();
test_parent_child_with_blockers();
}
fn test_empty_block_collapse_through() {
println!("\n=== Test 1: Empty Block Collapse Through ===");
let html = r#"
<!DOCTYPE html>
<html>
<body style="margin: 0; padding: 20px;">
<div style="margin-bottom: 20px; height: 50px; background: lightblue;">Box 1</div>
<div style="margin-top: 10px; margin-bottom: 30px;"></div>
<div style="margin-top: 15px; height: 50px; background: lightgreen;">Box 2</div>
</body>
</html>
"#;
println!("Expected: Box1(margin-bottom: 20px) -> EmptyDiv(10/30px) -> Box2(margin-top: 15px)");
println!(" Empty div margins collapse with each other: max(10, 30) = 30px");
println!(" Then collapse with siblings: max(20, 30, 15) = 30px");
println!(" Expected gap: 30px (not 20+10+30+15=75px)");
let images = BTreeMap::new();
let fonts = BTreeMap::new();
let options = GeneratePdfOptions::default();
let mut warnings = Vec::new();
match PdfDocument::from_html(&html, &images, &fonts, &options, &mut warnings) {
Ok(doc) => {
let mut save_warnings = Vec::new();
let pdf_bytes = doc.save(&PdfSaveOptions::default(), &mut save_warnings);
std::fs::write("margin_collapse_empty_through.pdf", &pdf_bytes).unwrap();
println!("[OK] Generated: margin_collapse_empty_through.pdf");
}
Err(e) => println!("[ERROR] Error: {:?}", e),
}
}
fn test_parent_child_top_escape() {
println!("\n=== Test 2: Parent-Child Top Margin Escape ===");
let html = r#"
<!DOCTYPE html>
<html>
<body style="margin: 0; padding: 20px;">
<div style="margin-top: 20px; background: lightgray;">
<div style="margin-top: 30px; height: 50px; background: lightblue;">Child (margin-top: 30px)</div>
</div>
</body>
</html>
"#;
println!("Expected: Parent has no border/padding, so child's margin-top escapes");
println!(" Child margin-top: 30px should 'escape' outside parent");
println!(" Parent's margin-top: 20px collapses with child's 30px");
println!(" Result: max(20, 30) = 30px from body to child content");
let images = BTreeMap::new();
let fonts = BTreeMap::new();
let options = GeneratePdfOptions::default();
let mut warnings = Vec::new();
match PdfDocument::from_html(&html, &images, &fonts, &options, &mut warnings) {
Ok(doc) => {
let mut save_warnings = Vec::new();
let pdf_bytes = doc.save(&PdfSaveOptions::default(), &mut save_warnings);
std::fs::write("margin_collapse_parent_child_top.pdf", &pdf_bytes).unwrap();
println!("[OK] Generated: margin_collapse_parent_child_top.pdf");
}
Err(e) => println!("[ERROR] Error: {:?}", e),
}
}
fn test_parent_child_bottom_escape() {
println!("\n=== Test 3: Parent-Child Bottom Margin Escape ===");
let html = r#"
<!DOCTYPE html>
<html>
<body style="margin: 0; padding: 20px;">
<div style="margin-bottom: 20px; background: lightgray;">
<div style="margin-bottom: 30px; height: 50px; background: lightblue;">Child (margin-bottom: 30px)</div>
</div>
<div style="margin-top: 15px; height: 50px; background: lightgreen;">Next sibling</div>
</body>
</html>
"#;
println!("Expected: Parent has no border/padding/height, so child's margin-bottom escapes");
println!(" Child margin-bottom: 30px escapes parent");
println!(" Parent margin-bottom: 20px collapses with child's 30px");
println!(" Result collapses with next sibling: max(30, 15) = 30px gap");
let images = BTreeMap::new();
let fonts = BTreeMap::new();
let options = GeneratePdfOptions::default();
let mut warnings = Vec::new();
match PdfDocument::from_html(&html, &images, &fonts, &options, &mut warnings) {
Ok(doc) => {
let mut save_warnings = Vec::new();
let pdf_bytes = doc.save(&PdfSaveOptions::default(), &mut save_warnings);
std::fs::write("margin_collapse_parent_child_bottom.pdf", &pdf_bytes).unwrap();
println!("[OK] Generated: margin_collapse_parent_child_bottom.pdf");
}
Err(e) => println!("[ERROR] Error: {:?}", e),
}
}
fn test_complex_nested_collapse() {
println!("\n=== Test 4: Complex Nested Scenario ===");
let html = r#"
<!DOCTYPE html>
<html>
<body style="margin: 0; padding: 20px;">
<div style="margin-top: 10px; margin-bottom: 10px; background: #f0f0f0;">
<div style="margin-top: 20px; margin-bottom: 20px; background: #e0e0e0;">
<div style="margin-top: 30px; margin-bottom: 30px; height: 50px; background: lightblue;">Inner content</div>
</div>
</div>
<div style="margin-top: 15px; height: 50px; background: lightgreen;">After</div>
</body>
</html>
"#;
println!("Expected: Triple-nested margin escape");
println!(" Top: max(10, 20, 30) = 30px from body to inner content");
println!(" Bottom: max(30, 20, 10, 15) = 30px from inner to after");
let images = BTreeMap::new();
let fonts = BTreeMap::new();
let options = GeneratePdfOptions::default();
let mut warnings = Vec::new();
match PdfDocument::from_html(&html, &images, &fonts, &options, &mut warnings) {
Ok(doc) => {
let mut save_warnings = Vec::new();
let pdf_bytes = doc.save(&PdfSaveOptions::default(), &mut save_warnings);
std::fs::write("margin_collapse_complex_nested.pdf", &pdf_bytes).unwrap();
println!("[OK] Generated: margin_collapse_complex_nested.pdf");
}
Err(e) => println!("[ERROR] Error: {:?}", e),
}
}
fn test_multiple_empty_blocks() {
println!("\n=== Test 5: Multiple Empty Blocks ===");
let html = r#"
<!DOCTYPE html>
<html>
<body style="margin: 0; padding: 20px;">
<div style="margin-bottom: 20px; height: 50px; background: lightblue;">Box 1</div>
<div style="margin-top: 10px; margin-bottom: 25px;"></div>
<div style="margin-top: 15px; margin-bottom: 30px;"></div>
<div style="margin-top: 12px; height: 50px; background: lightgreen;">Box 2</div>
</body>
</html>
"#;
println!("Expected: Multiple empty blocks collapse through");
println!(" Empty1: max(10, 25) = 25px");
println!(" Empty2: max(15, 30) = 30px");
println!(" All collapse: max(20, 25, 30, 12) = 30px total gap");
let images = BTreeMap::new();
let fonts = BTreeMap::new();
let options = GeneratePdfOptions::default();
let mut warnings = Vec::new();
match PdfDocument::from_html(&html, &images, &fonts, &options, &mut warnings) {
Ok(doc) => {
let mut save_warnings = Vec::new();
let pdf_bytes = doc.save(&PdfSaveOptions::default(), &mut save_warnings);
std::fs::write("margin_collapse_multiple_empty.pdf", &pdf_bytes).unwrap();
println!("[OK] Generated: margin_collapse_multiple_empty.pdf");
}
Err(e) => println!("[ERROR] Error: {:?}", e),
}
}
fn test_parent_child_with_blockers() {
println!("\n=== Test 6: Parent-Child with Blockers ===");
let html = r#"
<!DOCTYPE html>
<html>
<body style="margin: 0; padding: 20px;">
<div style="margin-top: 20px; background: #f0f0f0;">
<div style="margin-top: 30px; height: 50px; background: lightblue;">Child (should escape)</div>
</div>
<div style="height: 30px;"></div>
<div style="margin-top: 20px; border-top: 2px solid black; background: #e0e0e0;">
<div style="margin-top: 30px; height: 50px; background: lightblue;">Child (should NOT escape)</div>
</div>
</body>
</html>
"#;
println!("Expected:");
println!(" First parent: No blocker, child margin escapes = 30px from body");
println!(" Second parent: Border blocker, margins don't collapse = 20px + 30px = 50px");
let images = BTreeMap::new();
let fonts = BTreeMap::new();
let options = GeneratePdfOptions::default();
let mut warnings = Vec::new();
match PdfDocument::from_html(&html, &images, &fonts, &options, &mut warnings) {
Ok(doc) => {
let mut save_warnings = Vec::new();
let pdf_bytes = doc.save(&PdfSaveOptions::default(), &mut save_warnings);
std::fs::write("margin_collapse_parent_blockers.pdf", &pdf_bytes).unwrap();
println!("[OK] Generated: margin_collapse_parent_blockers.pdf");
}
Err(e) => println!("[ERROR] Error: {:?}", e),
}
}