office-toolkit 1.0.0

A Rust library to create, read, and modify Microsoft Office documents (Word/Excel/PowerPoint) in the modern Open XML (OOXML) format.
Documentation
//! PowerPoint theme, embedded fonts, and document metadata. Theme and
//! metadata both apply to a whole presentation, so this example writes
//! two small files rather than two slides of a single one.
//!
//! Run with: `cargo run -p office-toolkit --example pptx_theme_and_metadata`

use std::path::{Path, PathBuf};

use office_toolkit::SaveToFile;
use office_toolkit::drawing::{
    Color, Fill, ShapeProperties, TextBody, TextParagraph, TextRun, TextRunProperties, Transform2D,
};
use office_toolkit::powerpoint::{
    AutoShape, ColorScheme, CustomPropertyValue, DocumentProperties, EmbeddedFont, FontScheme,
    Shape, Theme,
};
use office_toolkit::prelude::*;

fn main() -> office_toolkit::Result<()> {
    // A custom theme and an embedded font. `Theme`/`ColorScheme`/
    // `FontScheme` have no `with_*` builders in this crate — only
    // `office_default()` or plain struct literals.
    let custom_theme = Theme {
        name: "Custom Brand".to_string(),
        colors: ColorScheme {
            dark1: "1B1B1B".to_string(),
            light1: "FFFFFF".to_string(),
            dark2: "0A2540".to_string(),
            light2: "F5F5F5".to_string(),
            accent1: "0A2540".to_string(),
            accent2: "E8622C".to_string(),
            accent3: "2E86AB".to_string(),
            accent4: "6FB98F".to_string(),
            accent5: "F4A259".to_string(),
            accent6: "8E4585".to_string(),
            hyperlink: "1155CC".to_string(),
            followed_hyperlink: "6B3FA0".to_string(),
        },
        fonts: FontScheme {
            major_latin: "Georgia".to_string(),
            minor_latin: "Verdana".to_string(),
        },
    };

    // Placeholder bytes — a real .pptx would carry genuine TrueType/
    // OpenType font data here.
    let embedded_font = EmbeddedFont::new("Custom Sans")
        .with_regular(vec![0u8; 16])
        .with_bold(vec![0u8; 16]);

    // A slide with no shapes at all renders as a blank page — nothing shows
    // the custom theme actually took effect. `drawing::Color` has no
    // scheme-relative variant yet (`<a:schemeClr>` isn't modeled, see that
    // enum's own doc comment), so this can't reference the theme's accent1
    // symbolically; it uses the same literal hex values the theme itself
    // was built from, purely so the slide visibly matches the palette above.
    let title_shape = AutoShape::new(2, "Title")
        .with_properties(
            ShapeProperties::new()
                .with_transform(
                    Transform2D::new()
                        .with_offset(838_200, 838_200)
                        .with_extent(6_000_000, 1_500_000),
                )
                .with_fill(Fill::Solid(Color::Rgb(custom_theme.colors.accent1.clone()))),
        )
        .with_text_body(
            TextBody::new().with_paragraph(
                TextParagraph::new().with_run(TextRun::text_with_properties(
                    "Custom Brand theme — Georgia / Verdana, accent1 = #0A2540",
                    TextRunProperties::new()
                        .with_font_family(custom_theme.fonts.major_latin.clone())
                        .with_font_size_points(24.0)
                        .with_fill(Fill::Solid(Color::Rgb(custom_theme.colors.light1.clone()))),
                )),
            ),
        );

    let themed_presentation = Presentation::new()
        .with_theme(custom_theme)
        .with_embedded_font(embedded_font)
        .with_slide(Slide::new().with_shape(Shape::AutoShape(title_shape)));
    let theme_path = output_path("pptx_theme.pptx");
    themed_presentation.save_to_file(&theme_path)?;
    println!("Wrote {}", theme_path.display());

    // Document metadata (docProps/core.xml, app.xml, and custom.xml).
    let properties = DocumentProperties::new()
        .with_title("Metadata example")
        .with_author("Example Author")
        .with_subject("Presentation-level features")
        .with_custom_property(
            "ProjectCode",
            CustomPropertyValue::Text("PPTX-META".to_string()),
        );
    // Same reasoning as `title_shape` above: an empty slide would render
    // blank, so a text shape spells out where the metadata actually lives.
    let metadata_shape = AutoShape::new(2, "Note")
        .with_properties(ShapeProperties::new().with_transform(Transform2D::new().with_offset(838_200, 838_200).with_extent(7_000_000, 1_000_000)))
        .with_text_body(TextBody::new().with_paragraph(TextParagraph::new().with_run(TextRun::text(
            "This presentation's metadata is in File > Info (title, author, subject, and a custom \"ProjectCode\" property) — not on this slide.",
        ))));
    let metadata_presentation = Presentation::new()
        .with_properties(properties)
        .with_slide(Slide::new().with_shape(Shape::AutoShape(metadata_shape)));
    let metadata_path = output_path("pptx_metadata.pptx");
    metadata_presentation.save_to_file(&metadata_path)?;
    println!("Wrote {}", metadata_path.display());

    Ok(())
}

/// Every example in this directory writes its output under
/// `tests-data/output/`, resolved relative to this crate's own manifest so
/// it works no matter what directory `cargo run` was invoked from.
fn output_path(filename: &str) -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../../tests-data/output")
        .join(filename)
}