Skip to main content

pptx_media/
pptx_media.rs

1//! PowerPoint media: an embedded picture, a picture with an external link
2//! alongside its embedded preview, a video clip with a poster frame, and
3//! an embedded chart. One slide per feature.
4//!
5//! Run with: `cargo run -p office-toolkit --example pptx_media`
6
7use std::path::{Path, PathBuf};
8
9use office_toolkit::SaveToFile;
10use office_toolkit::chart::{
11    Axis, AxisDataSource, AxisPosition, BarChart, BarDirection, BarSeries, CategoryAxis, Chart,
12    ChartSpace, ChartType, NumericData, NumericDataSource, NumericPoint, PlotArea, StringData,
13    StringDataSource, StringPoint, ValueAxis,
14};
15use office_toolkit::powerpoint::{
16    MediaFormat, Picture, PictureFormat, Shape, SlideChart, SlideMedia,
17};
18use office_toolkit::prelude::*;
19
20fn main() -> office_toolkit::Result<()> {
21    let path = output_path("pptx_media.pptx");
22    let image_bytes = std::fs::read(image_fixture_path())?;
23
24    let embedded_picture = Picture::new(
25        2,
26        "Embedded picture",
27        image_bytes.clone(),
28        PictureFormat::Png,
29        3_000_000,
30        2_000_000,
31    )
32    .with_offset(838_200, 838_200)
33    .with_description("Project test image");
34
35    let linked_picture = Picture::new(
36        2,
37        "Externally linked picture",
38        image_bytes,
39        PictureFormat::Png,
40        3_000_000,
41        2_000_000,
42    )
43    .with_offset(838_200, 838_200)
44    .with_external_link("https://example.com/original-image.png");
45
46    // Placeholder bytes — a real .pptx would carry a genuine video file
47    // here. This example only demonstrates the model/writer plumbing.
48    let poster_image = vec![0x89, 0x50, 0x4E, 0x47]; // fake, just enough bytes to have *some* data
49    let media = SlideMedia::new(
50        2,
51        "Video clip",
52        vec![0u8; 16],
53        MediaFormat::Mp4,
54        4_000_000,
55        2_250_000,
56    )
57    .with_offset(838_200, 838_200)
58    .with_poster_image(poster_image, PictureFormat::Png);
59
60    let chart = SlideChart::new(
61        2,
62        "Quarterly revenue",
63        sample_chart_space(),
64        6_000_000,
65        3_500_000,
66    )
67    .with_offset(838_200, 838_200);
68
69    let presentation = Presentation::new()
70        .with_slide(Slide::new().with_shape(Shape::Picture(embedded_picture)))
71        .with_slide(Slide::new().with_shape(Shape::Picture(linked_picture)))
72        .with_slide(Slide::new().with_shape(Shape::Media(media)))
73        .with_slide(Slide::new().with_shape(Shape::Chart(Box::new(chart))));
74
75    presentation.save_to_file(&path)?;
76    println!("Wrote {}", path.display());
77    Ok(())
78}
79
80/// A minimal bar chart with literal (not cell-referenced) data.
81fn sample_chart_space() -> ChartSpace {
82    let categories = StringData::new()
83        .with_point(StringPoint::new(0, "Q1"))
84        .with_point(StringPoint::new(1, "Q2"))
85        .with_point(StringPoint::new(2, "Q3"));
86    let values = NumericData::new()
87        .with_point(NumericPoint::new(0, "1200"))
88        .with_point(NumericPoint::new(1, "1350"))
89        .with_point(NumericPoint::new(2, "980"));
90
91    let series = BarSeries::new(0, 0)
92        .with_categories(AxisDataSource::String(StringDataSource::literal(
93            categories,
94        )))
95        .with_values(NumericDataSource::literal(values));
96
97    let bar_chart = BarChart::new(BarDirection::Column, 1, 2).with_series(series);
98    let category_axis = CategoryAxis::new(1, AxisPosition::Bottom, 2);
99    let value_axis = ValueAxis::new(2, AxisPosition::Left, 1);
100
101    let plot_area = PlotArea::new()
102        .with_chart(ChartType::Bar(bar_chart))
103        .with_axis(Axis::Category(category_axis))
104        .with_axis(Axis::Value(value_axis));
105
106    ChartSpace::new(Chart::new(plot_area))
107}
108
109/// The project's own Word test fixture, reused here too since it's a
110/// simple, small, real PNG.
111fn image_fixture_path() -> PathBuf {
112    Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests-data/word/test_image.png")
113}
114
115/// Every example in this directory writes its output under
116/// `tests-data/output/`, resolved relative to this crate's own manifest so
117/// it works no matter what directory `cargo run` was invoked from.
118fn output_path(filename: &str) -> PathBuf {
119    Path::new(env!("CARGO_MANIFEST_DIR"))
120        .join("../../tests-data/output")
121        .join(filename)
122}