use ooxml_pml::{Presentation, PresentationBuilder, TableBuilder, TextRun};
use std::io::Cursor;
fn write_and_read(builder: PresentationBuilder) -> Presentation<Cursor<Vec<u8>>> {
let mut buf = Cursor::new(Vec::new());
builder.write(&mut buf).expect("write should succeed");
buf.set_position(0);
Presentation::from_reader(buf).expect("read should succeed")
}
#[test]
fn test_presentation_roundtrip() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_title("Slide One");
builder.add_slide().add_title("Slide Two");
let pres = write_and_read(builder);
assert_eq!(pres.slide_count(), 2);
}
#[test]
fn test_slide_text_content() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_text("Hello from ooxml-pml");
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
let text = slide.text();
assert!(
text.contains("Hello from ooxml-pml"),
"expected text not found; got: {:?}",
text
);
}
#[test]
fn test_slide_title() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_title("My Presentation Title");
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
use ooxml_pml::ShapeExt;
let title_shape = slide
.shapes()
.iter()
.find(|s| s.name() == "Title")
.expect("title shape should exist");
let title_text = title_shape.text().unwrap_or_default();
assert!(
title_text.contains("My Presentation Title"),
"title text mismatch; got: {:?}",
title_text
);
}
#[test]
fn test_slide_ordering() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_title("First");
builder.add_slide().add_title("Second");
builder.add_slide().add_title("Third");
let mut pres = write_and_read(builder);
assert_eq!(pres.slide_count(), 3);
let slides = pres.slides().unwrap();
assert_eq!(slides[0].index(), 0);
assert_eq!(slides[1].index(), 1);
assert_eq!(slides[2].index(), 2);
assert!(slides[0].text().contains("First"), "slide 0 text wrong");
assert!(slides[1].text().contains("Second"), "slide 1 text wrong");
assert!(slides[2].text().contains("Third"), "slide 2 text wrong");
}
#[test]
fn test_speaker_notes() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_title("Notes Demo");
slide.set_notes("Remember to speak slowly.");
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(slide.has_notes(), "slide should have notes");
let notes = slide.notes().expect("notes should be Some");
assert!(
notes.contains("Remember to speak slowly."),
"notes text mismatch; got: {:?}",
notes
);
}
#[test]
fn test_no_speaker_notes() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_title("No Notes");
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(!slide.has_notes());
assert!(slide.notes().is_none());
}
#[test]
fn test_image_roundtrip() {
let png_bytes: &[u8] = &[
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x7E, 0x9B, 0x55, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9C, 0x62, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xE2, 0x21, 0xBC, 0x33, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
];
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_image(
png_bytes.to_vec(),
914400,
914400, 914400,
914400, );
}
let mut buf = Cursor::new(Vec::new());
builder.write(&mut buf).expect("write should succeed");
buf.set_position(0);
let mut pres = Presentation::from_reader(buf).expect("read should succeed");
let slide = pres.slide(0).unwrap();
let pictures = slide.pictures();
assert_eq!(pictures.len(), 1, "expected one picture");
let img = pres.get_image_data(&slide, &pictures[0]).unwrap();
assert_eq!(img.data, png_bytes, "image bytes should match");
assert_eq!(img.content_type, "image/png");
}
#[cfg(feature = "dml-tables")]
#[test]
fn test_table_roundtrip() {
use ooxml_dml::TableCellExt;
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_title("Table Slide");
let table = TableBuilder::new()
.name("Test Table")
.add_row(["Alpha", "Beta", "Gamma"])
.add_row(["1", "2", "3"])
.add_row(["X", "Y", "Z"]);
slide.add_table(table, 914400, 1828800, 7315200, 1828800);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(slide.has_tables(), "slide should have tables");
assert_eq!(slide.table_count(), 1);
let table = slide.table(0).unwrap();
assert_eq!(table.row_count(), 3, "row count mismatch");
assert_eq!(table.col_count(), 3, "col count mismatch");
assert_eq!(table.cell(0, 0).unwrap().text(), "Alpha");
assert_eq!(table.cell(0, 1).unwrap().text(), "Beta");
assert_eq!(table.cell(0, 2).unwrap().text(), "Gamma");
assert_eq!(table.cell(1, 0).unwrap().text(), "1");
assert_eq!(table.cell(2, 2).unwrap().text(), "Z");
}
#[cfg(feature = "dml-tables")]
#[test]
fn test_table_text_grid() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
let table = TableBuilder::new()
.add_row(["Row0Col0", "Row0Col1"])
.add_row(["Row1Col0", "Row1Col1"]);
slide.add_table(table, 0, 0, 9144000, 6858000);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
let grid = slide.table(0).unwrap().to_text_grid();
assert_eq!(grid.len(), 2);
assert_eq!(grid[0], vec!["Row0Col0", "Row0Col1"]);
assert_eq!(grid[1], vec!["Row1Col0", "Row1Col1"]);
}
#[test]
fn test_empty_table_not_added() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_table(TableBuilder::new(), 0, 0, 9144000, 6858000);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(!slide.has_tables(), "no table should have been added");
}
#[test]
fn test_hyperlink_roundtrip() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_hyperlink(
"Visit Rust",
"https://www.rust-lang.org",
457200,
1600200,
8229600,
457200,
);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(slide.has_hyperlinks(), "slide should have hyperlinks");
let links = slide.hyperlinks();
assert_eq!(links.len(), 1);
assert_eq!(links[0].text, "Visit Rust");
let url = pres.resolve_hyperlink(&slide, &links[0].rel_id).unwrap();
assert_eq!(url, "https://www.rust-lang.org");
}
#[test]
fn test_get_hyperlinks_with_urls() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_hyperlink("Rust", "https://www.rust-lang.org", 0, 0, 1000, 1000);
slide.add_hyperlink("Crates", "https://crates.io", 0, 100000, 1000, 1000);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
let links = pres.get_hyperlinks_with_urls(&slide).unwrap();
assert_eq!(links.len(), 2);
let map: std::collections::HashMap<_, _> = links.into_iter().collect();
assert_eq!(map["Rust"], "https://www.rust-lang.org");
assert_eq!(map["Crates"], "https://crates.io");
}
#[test]
fn test_mixed_text_runs_roundtrip() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_text_with_runs(
vec![
TextRun::text("Check out "),
TextRun::hyperlink("the docs", "https://docs.rs"),
TextRun::text(" for more."),
],
457200,
1600200,
8229600,
457200,
);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(slide.has_hyperlinks());
let links = slide.hyperlinks();
assert_eq!(links.len(), 1);
assert_eq!(links[0].text, "the docs");
let url = pres.resolve_hyperlink(&slide, &links[0].rel_id).unwrap();
assert_eq!(url, "https://docs.rs");
}
#[cfg(feature = "pml-charts")]
#[test]
fn test_chart_rel_ids_empty_for_normal_slide() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_text("No chart here");
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(
slide.chart_rel_ids().is_empty(),
"expected no chart rel IDs on a plain text slide"
);
}
#[cfg(feature = "pml-charts")]
#[test]
fn test_smartart_rel_ids_empty_for_normal_slide() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_text("No SmartArt here");
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(
slide.smartart_rel_ids().is_empty(),
"expected no SmartArt rel IDs on a plain text slide"
);
}
#[test]
fn test_slide_count_zero() {
let builder = PresentationBuilder::new();
let pres = write_and_read(builder);
assert_eq!(pres.slide_count(), 0);
}
#[test]
fn test_slide_count_one() {
let mut builder = PresentationBuilder::new();
builder.add_slide();
let pres = write_and_read(builder);
assert_eq!(pres.slide_count(), 1);
}
#[test]
fn test_slide_count_many() {
let mut builder = PresentationBuilder::new();
for i in 0..10 {
builder.add_slide().add_title(format!("Slide {}", i));
}
let pres = write_and_read(builder);
assert_eq!(pres.slide_count(), 10);
}
#[test]
fn test_slide_master_and_layout_present() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_title("Master Test");
let pres = write_and_read(builder);
assert_eq!(pres.slide_masters().len(), 1, "should have 1 slide master");
assert_eq!(pres.slide_layouts().len(), 1, "should have 1 slide layout");
let master = &pres.slide_masters()[0];
assert_eq!(master.layout_count(), 1, "master should reference 1 layout");
}
#[test]
fn test_layout_by_name() {
let mut builder = PresentationBuilder::new();
builder.add_slide();
let pres = write_and_read(builder);
let found = pres.layout_by_name("Blank");
assert!(found.is_some(), "expected to find layout named 'Blank'");
}
#[test]
fn test_slide_has_layout_rel_id() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_title("Layout Ref Test");
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(
slide.layout_rel_id().is_some(),
"slide should reference a layout"
);
}
#[test]
fn test_slide_index_accessor() {
let mut builder = PresentationBuilder::new();
for _ in 0..3 {
builder.add_slide();
}
let mut pres = write_and_read(builder);
for i in 0..3 {
let slide = pres.slide(i).unwrap();
assert_eq!(slide.index(), i);
}
}
#[test]
fn test_slide_out_of_range() {
let mut builder = PresentationBuilder::new();
builder.add_slide();
let mut pres = write_and_read(builder);
assert!(pres.slide(1).is_err(), "index 1 should be out of range");
assert!(pres.slide(99).is_err(), "index 99 should be out of range");
}
#[test]
fn test_widescreen_presentation() {
let mut builder = PresentationBuilder::new();
builder.set_widescreen();
builder.add_slide().add_title("Widescreen");
let pres = write_and_read(builder);
assert_eq!(pres.slide_count(), 1);
}
#[cfg(feature = "pml-transitions")]
#[test]
fn test_slide_transition_roundtrip() {
use ooxml_pml::{SlideTransition, TransitionSpeed, TransitionType};
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_title("Transition Slide");
slide.set_transition(
SlideTransition::new(TransitionType::Wipe)
.with_speed(TransitionSpeed::Fast)
.with_advance_after(2000),
);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(slide.has_transition(), "slide should have a transition");
let t = slide.transition().unwrap();
assert_eq!(t.transition_type, Some(TransitionType::Wipe));
assert_eq!(t.speed, TransitionSpeed::Fast);
assert_eq!(t.advance_time_ms, Some(2000));
}
#[cfg(feature = "pml-transitions")]
#[test]
fn test_no_transition() {
let mut builder = PresentationBuilder::new();
builder.add_slide().add_title("No Transition");
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(!slide.has_transition());
assert!(slide.transition().is_none());
}
#[test]
fn test_add_text_at_roundtrip() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_text_at("Positioned text", 457200, 457200, 4572000, 914400);
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
assert!(
slide.text().contains("Positioned text"),
"text not found; got: {:?}",
slide.text()
);
}
#[test]
fn test_multiple_text_elements() {
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_title("Title Here");
slide.add_text("Body line one");
slide.add_text("Body line two");
}
let mut pres = write_and_read(builder);
let slide = pres.slide(0).unwrap();
let text = slide.text();
assert!(text.contains("Title Here"), "title missing");
assert!(text.contains("Body line one"), "body line 1 missing");
assert!(text.contains("Body line two"), "body line 2 missing");
}
#[test]
fn test_jpeg_image_roundtrip() {
let jpeg_bytes: Vec<u8> = vec![
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xD9, ];
let mut builder = PresentationBuilder::new();
{
let slide = builder.add_slide();
slide.add_image(jpeg_bytes.clone(), 0, 0, 914400, 914400);
}
let mut buf = Cursor::new(Vec::new());
builder.write(&mut buf).expect("write");
buf.set_position(0);
let mut pres = Presentation::from_reader(buf).expect("read");
let slide = pres.slide(0).unwrap();
let pictures = slide.pictures();
assert_eq!(pictures.len(), 1);
let img = pres.get_image_data(&slide, &pictures[0]).unwrap();
assert_eq!(img.data, jpeg_bytes);
assert_eq!(img.content_type, "image/jpeg");
}
#[test]
fn test_notes_independence() {
let mut builder = PresentationBuilder::new();
{
let s0 = builder.add_slide();
s0.add_title("Slide A");
s0.set_notes("Notes for A");
}
{
let s1 = builder.add_slide();
s1.add_title("Slide B");
}
{
let s2 = builder.add_slide();
s2.add_title("Slide C");
s2.set_notes("Notes for C");
}
let mut pres = write_and_read(builder);
let s0 = pres.slide(0).unwrap();
let s1 = pres.slide(1).unwrap();
let s2 = pres.slide(2).unwrap();
assert!(s0.has_notes());
assert_eq!(s0.notes().unwrap(), "Notes for A");
assert!(!s1.has_notes());
assert!(s1.notes().is_none());
assert!(s2.has_notes());
assert_eq!(s2.notes().unwrap(), "Notes for C");
}
#[test]
fn test_slides_returns_all() {
let mut builder = PresentationBuilder::new();
for i in 0..5 {
builder.add_slide().add_title(format!("Slide {}", i));
}
let mut pres = write_and_read(builder);
let slides = pres.slides().unwrap();
assert_eq!(slides.len(), 5);
for (i, slide) in slides.iter().enumerate() {
assert_eq!(slide.index(), i);
}
}