mirror of
https://github.com/duhanbalci/dreport.git
synced 2026-07-01 18:39:16 +00:00
325 lines
11 KiB
Rust
325 lines
11 KiB
Rust
//! PDF render integration tests.
|
|
//! Only compiled on non-WASM targets since pdf_render uses krilla (native only).
|
|
|
|
#![cfg(not(target_arch = "wasm32"))]
|
|
|
|
use dreport_core::models::*;
|
|
use dreport_layout::compute_layout;
|
|
|
|
mod common;
|
|
use common::load_test_fonts;
|
|
|
|
fn simple_template() -> Template {
|
|
Template {
|
|
id: "pdf_test".to_string(),
|
|
name: "PDF Test".to_string(),
|
|
page: PageSettings {
|
|
width: 210.0,
|
|
height: 297.0,
|
|
},
|
|
fonts: vec!["Noto Sans".to_string()],
|
|
header: None,
|
|
footer: None,
|
|
format_config: None,
|
|
locale: None,
|
|
root: ContainerElement {
|
|
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
|
direction: "column".to_string(),
|
|
gap: 5.0,
|
|
padding: Padding {
|
|
top: 15.0,
|
|
right: 15.0,
|
|
bottom: 15.0,
|
|
left: 15.0,
|
|
},
|
|
align: "stretch".to_string(),
|
|
justify: "start".to_string(),
|
|
style: ContainerStyle::default(),
|
|
break_inside: "auto".to_string(),
|
|
children: vec![TemplateElement::StaticText(StaticTextElement {
|
|
base: ElementBase::flow("title".to_string(), SizeConstraint {
|
|
width: SizeValue::Fr { value: 1.0 },
|
|
height: SizeValue::Auto,
|
|
..Default::default()
|
|
}),
|
|
style: TextStyle {
|
|
font_size: Some(18.0),
|
|
font_weight: Some("bold".to_string()),
|
|
..Default::default()
|
|
},
|
|
content: "PDF Render Test".to_string(),
|
|
})],
|
|
},
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_render_pdf_produces_valid_output() {
|
|
let template = simple_template();
|
|
let data = serde_json::json!({});
|
|
let fonts = load_test_fonts();
|
|
|
|
let layout = compute_layout(&template, &data, &fonts).unwrap();
|
|
let pdf_bytes = dreport_layout::pdf_render::render_pdf(&layout, &fonts).unwrap();
|
|
|
|
// PDF should not be empty
|
|
assert!(!pdf_bytes.is_empty(), "PDF output should not be empty");
|
|
|
|
// PDF should start with %PDF magic bytes
|
|
assert!(
|
|
pdf_bytes.starts_with(b"%PDF"),
|
|
"PDF output should start with %PDF magic bytes, got: {:?}",
|
|
&pdf_bytes[..std::cmp::min(10, pdf_bytes.len())]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_render_pdf_with_multiple_elements() {
|
|
let template = Template {
|
|
id: "pdf_multi".to_string(),
|
|
name: "PDF Multi".to_string(),
|
|
page: PageSettings {
|
|
width: 210.0,
|
|
height: 297.0,
|
|
},
|
|
fonts: vec!["Noto Sans".to_string()],
|
|
header: None,
|
|
footer: None,
|
|
format_config: None,
|
|
locale: None,
|
|
root: ContainerElement {
|
|
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
|
direction: "column".to_string(),
|
|
gap: 5.0,
|
|
padding: Padding {
|
|
top: 15.0,
|
|
right: 15.0,
|
|
bottom: 15.0,
|
|
left: 15.0,
|
|
},
|
|
align: "stretch".to_string(),
|
|
justify: "start".to_string(),
|
|
style: ContainerStyle::default(),
|
|
break_inside: "auto".to_string(),
|
|
children: vec![
|
|
TemplateElement::StaticText(StaticTextElement {
|
|
base: ElementBase::flow("header".to_string(), SizeConstraint {
|
|
width: SizeValue::Fr { value: 1.0 },
|
|
height: SizeValue::Auto,
|
|
..Default::default()
|
|
}),
|
|
style: TextStyle {
|
|
font_size: Some(16.0),
|
|
font_weight: Some("bold".to_string()),
|
|
..Default::default()
|
|
},
|
|
content: "FATURA".to_string(),
|
|
}),
|
|
TemplateElement::Line(LineElement {
|
|
base: ElementBase::flow("sep".to_string(), SizeConstraint {
|
|
width: SizeValue::Fr { value: 1.0 },
|
|
height: SizeValue::Auto,
|
|
..Default::default()
|
|
}),
|
|
style: LineStyle {
|
|
stroke_color: Some("#000000".to_string()),
|
|
stroke_width: Some(0.5),
|
|
},
|
|
}),
|
|
TemplateElement::StaticText(StaticTextElement {
|
|
base: ElementBase::flow("body".to_string(), SizeConstraint {
|
|
width: SizeValue::Fr { value: 1.0 },
|
|
height: SizeValue::Auto,
|
|
..Default::default()
|
|
}),
|
|
style: TextStyle {
|
|
font_size: Some(11.0),
|
|
..Default::default()
|
|
},
|
|
content: "Bu bir test belgesidir.".to_string(),
|
|
}),
|
|
],
|
|
},
|
|
};
|
|
|
|
let data = serde_json::json!({});
|
|
let fonts = load_test_fonts();
|
|
|
|
let layout = compute_layout(&template, &data, &fonts).unwrap();
|
|
let pdf_bytes = dreport_layout::pdf_render::render_pdf(&layout, &fonts).unwrap();
|
|
|
|
assert!(!pdf_bytes.is_empty());
|
|
assert!(pdf_bytes.starts_with(b"%PDF"));
|
|
|
|
// A PDF with multiple elements should be reasonably sized
|
|
assert!(
|
|
pdf_bytes.len() > 100,
|
|
"PDF with multiple elements should be >100 bytes, got {}",
|
|
pdf_bytes.len()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_render_pdf_with_container_styles() {
|
|
let template = Template {
|
|
id: "pdf_styled".to_string(),
|
|
name: "PDF Styled".to_string(),
|
|
page: PageSettings {
|
|
width: 210.0,
|
|
height: 297.0,
|
|
},
|
|
fonts: vec!["Noto Sans".to_string()],
|
|
header: None,
|
|
footer: None,
|
|
format_config: None,
|
|
locale: None,
|
|
root: ContainerElement {
|
|
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
|
direction: "column".to_string(),
|
|
gap: 0.0,
|
|
padding: Padding {
|
|
top: 20.0,
|
|
right: 20.0,
|
|
bottom: 20.0,
|
|
left: 20.0,
|
|
},
|
|
align: "stretch".to_string(),
|
|
justify: "start".to_string(),
|
|
style: ContainerStyle {
|
|
background_color: Some("#f0f0f0".to_string()),
|
|
border_color: Some("#333333".to_string()),
|
|
border_width: Some(1.0),
|
|
..Default::default()
|
|
},
|
|
break_inside: "auto".to_string(),
|
|
children: vec![TemplateElement::StaticText(StaticTextElement {
|
|
base: ElementBase::flow("text".to_string(), SizeConstraint {
|
|
width: SizeValue::Fr { value: 1.0 },
|
|
height: SizeValue::Auto,
|
|
..Default::default()
|
|
}),
|
|
style: TextStyle {
|
|
font_size: Some(12.0),
|
|
color: Some("#ff0000".to_string()),
|
|
..Default::default()
|
|
},
|
|
content: "Styled text".to_string(),
|
|
})],
|
|
},
|
|
};
|
|
|
|
let data = serde_json::json!({});
|
|
let fonts = load_test_fonts();
|
|
|
|
let layout = compute_layout(&template, &data, &fonts).unwrap();
|
|
let pdf_bytes = dreport_layout::pdf_render::render_pdf(&layout, &fonts).unwrap();
|
|
|
|
assert!(!pdf_bytes.is_empty());
|
|
assert!(pdf_bytes.starts_with(b"%PDF"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_page_break_produces_multiple_pages() {
|
|
let template = Template {
|
|
id: "pb_test".to_string(),
|
|
name: "Page Break Test".to_string(),
|
|
page: PageSettings {
|
|
width: 210.0,
|
|
height: 297.0,
|
|
},
|
|
fonts: vec!["Noto Sans".to_string()],
|
|
header: None,
|
|
footer: None,
|
|
format_config: None,
|
|
locale: None,
|
|
root: ContainerElement {
|
|
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
|
direction: "column".to_string(),
|
|
gap: 5.0,
|
|
padding: Padding {
|
|
top: 15.0,
|
|
right: 15.0,
|
|
bottom: 15.0,
|
|
left: 15.0,
|
|
},
|
|
align: "stretch".to_string(),
|
|
justify: "start".to_string(),
|
|
style: ContainerStyle::default(),
|
|
break_inside: "auto".to_string(),
|
|
children: vec![
|
|
TemplateElement::StaticText(StaticTextElement {
|
|
base: ElementBase::flow("t1".to_string(), SizeConstraint {
|
|
width: SizeValue::Fr { value: 1.0 },
|
|
height: SizeValue::Auto,
|
|
..Default::default()
|
|
}),
|
|
style: TextStyle {
|
|
font_size: Some(18.0),
|
|
..Default::default()
|
|
},
|
|
content: "Page 1 content".to_string(),
|
|
}),
|
|
TemplateElement::PageBreak(PageBreakElement {
|
|
base: ElementBase::flow("pb1".to_string(), SizeConstraint::default()),
|
|
}),
|
|
TemplateElement::StaticText(StaticTextElement {
|
|
base: ElementBase::flow("t2".to_string(), SizeConstraint {
|
|
width: SizeValue::Fr { value: 1.0 },
|
|
height: SizeValue::Auto,
|
|
..Default::default()
|
|
}),
|
|
style: TextStyle {
|
|
font_size: Some(18.0),
|
|
..Default::default()
|
|
},
|
|
content: "Page 2 content".to_string(),
|
|
}),
|
|
],
|
|
},
|
|
};
|
|
|
|
let data = serde_json::json!({});
|
|
let fonts = load_test_fonts();
|
|
|
|
let layout = compute_layout(&template, &data, &fonts).unwrap();
|
|
|
|
println!("Layout pages: {}", layout.pages.len());
|
|
for (i, page) in layout.pages.iter().enumerate() {
|
|
println!("Page {}: {} elements", i, page.elements.len());
|
|
for el in &page.elements {
|
|
println!(
|
|
" - {} (type={}, y={:.1}mm, h={:.1}mm)",
|
|
el.id, el.element_type, el.y_mm, el.height_mm
|
|
);
|
|
}
|
|
}
|
|
|
|
assert_eq!(layout.pages.len(), 2, "Page break should produce 2 pages");
|
|
|
|
// Verify page 1 has t1 and page 2 has t2
|
|
let p1_ids: Vec<&str> = layout.pages[0]
|
|
.elements
|
|
.iter()
|
|
.map(|e| e.id.as_str())
|
|
.collect();
|
|
let p2_ids: Vec<&str> = layout.pages[1]
|
|
.elements
|
|
.iter()
|
|
.map(|e| e.id.as_str())
|
|
.collect();
|
|
println!("Page 1 IDs: {:?}", p1_ids);
|
|
println!("Page 2 IDs: {:?}", p2_ids);
|
|
|
|
assert!(p1_ids.contains(&"t1"), "Page 1 should contain t1");
|
|
assert!(p2_ids.contains(&"t2"), "Page 2 should contain t2");
|
|
|
|
// Render PDF and verify it's valid
|
|
let pdf_bytes = dreport_layout::pdf_render::render_pdf(&layout, &fonts).unwrap();
|
|
assert!(pdf_bytes.starts_with(b"%PDF"));
|
|
|
|
// Write PDF to temp dir for manual inspection
|
|
let out_path = std::env::temp_dir().join("dreport_test_page_break.pdf");
|
|
std::fs::write(&out_path, &pdf_bytes).unwrap();
|
|
println!("Wrote: {}", out_path.display());
|
|
}
|