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 text-in-shape formatting (from the shared `drawing` crate):
//! paragraph spacing/indent, bullets and auto-numbering, autofit and
//! centered text, casing/spacing/highlight/superscript, and a dynamic
//! text field. One slide per feature.
//!
//! Run with: `cargo run -p office-toolkit --example pptx_text_formatting`

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

use office_toolkit::SaveToFile;
use office_toolkit::drawing::{
    BulletKind, Color, ShapeProperties, TextAnchor, TextAutofit, TextBody, TextBodyProperties,
    TextCaps, TextParagraph, TextParagraphProperties, TextRun, TextRunProperties, TextSpacing,
    Transform2D,
};
use office_toolkit::powerpoint::{AutoShape, Shape};
use office_toolkit::prelude::*;

fn main() -> office_toolkit::Result<()> {
    let path = output_path("pptx_text_formatting.pptx");

    let spacing_shape = AutoShape::new(2, "Spacing")
        .with_properties(
            ShapeProperties::new().with_transform(
                Transform2D::new()
                    .with_offset(838_200, 838_200)
                    .with_extent(6_000_000, 1_500_000),
            ),
        )
        .with_text_body(
            TextBody::new().with_paragraph(
                TextParagraph::new()
                    .with_properties(
                        TextParagraphProperties::new()
                            .with_line_spacing(TextSpacing::Percent(150_000))
                            .with_space_before(TextSpacing::Points(1200))
                            .with_indent_emu(457_200)
                            .with_level(1),
                    )
                    .with_run(TextRun::text(
                        "Indented, 150% line spacing, 12pt space before.",
                    )),
            ),
        );

    let bullets_shape = AutoShape::new(2, "Bullets")
        .with_properties(
            ShapeProperties::new().with_transform(
                Transform2D::new()
                    .with_offset(838_200, 838_200)
                    .with_extent(6_000_000, 1_800_000),
            ),
        )
        .with_text_body(
            TextBody::new()
                .with_paragraph(
                    TextParagraph::new()
                        .with_properties(
                            TextParagraphProperties::new()
                                .with_bullet(BulletKind::Character("\u{2022}".to_string())),
                        )
                        .with_run(TextRun::text("Bulleted item")),
                )
                .with_paragraph(
                    TextParagraph::new()
                        .with_properties(TextParagraphProperties::new().with_bullet(
                            BulletKind::AutoNumber {
                                scheme: "arabicPeriod".to_string(),
                                start_at: None,
                            },
                        ))
                        .with_run(TextRun::text("Auto-numbered item")),
                ),
        );

    let autofit_shape = AutoShape::new(2, "Autofit")
        .with_text_box(true)
        .with_properties(
            ShapeProperties::new().with_transform(
                Transform2D::new()
                    .with_offset(838_200, 838_200)
                    .with_extent(4_000_000, 1_000_000),
            ),
        )
        .with_text_body(
            TextBody::new()
                .with_properties(
                    TextBodyProperties::new()
                        .with_autofit(TextAutofit::Shape)
                        .with_anchor(TextAnchor::Center)
                        .with_anchor_center(true),
                )
                .with_paragraph(TextParagraph::new().with_run(TextRun::text(
                    "This shape resizes to fit its text, centered both ways.",
                ))),
        );

    let casing_shape = AutoShape::new(2, "Casing")
        .with_properties(
            ShapeProperties::new().with_transform(
                Transform2D::new()
                    .with_offset(838_200, 838_200)
                    .with_extent(6_000_000, 1_500_000),
            ),
        )
        .with_text_body(
            TextBody::new().with_paragraph(
                TextParagraph::new()
                    .with_run(TextRun::text_with_properties(
                        "rendered in all caps, ",
                        TextRunProperties::new().with_text_caps(TextCaps::All),
                    ))
                    .with_run(TextRun::text_with_properties(
                        "widely spaced, ",
                        TextRunProperties::new().with_character_spacing_points(3.0),
                    ))
                    .with_run(TextRun::text_with_properties(
                        "highlighted",
                        TextRunProperties::new().with_highlight(Color::Rgb("FFFF00".to_string())),
                    ))
                    .with_run(TextRun::text_with_properties(
                        "2",
                        TextRunProperties::new().with_baseline_percent(30.0),
                    )),
            ),
        );

    let field_shape = AutoShape::new(2, "Field")
        .with_properties(
            ShapeProperties::new().with_transform(
                Transform2D::new()
                    .with_offset(838_200, 838_200)
                    .with_extent(3_000_000, 500_000),
            ),
        )
        .with_text_body(
            TextBody::new().with_paragraph(
                TextParagraph::new()
                    .with_run(TextRun::text("Slide "))
                    .with_run(TextRun::field(
                        "{7A9E3F10-4B6C-4D2E-9A1F-8C5D6E7F0123}",
                        Some("slidenum".to_string()),
                        "1",
                    )),
            ),
        );

    let presentation = Presentation::new()
        .with_slide(Slide::new().with_shape(Shape::AutoShape(spacing_shape)))
        .with_slide(Slide::new().with_shape(Shape::AutoShape(bullets_shape)))
        .with_slide(Slide::new().with_shape(Shape::AutoShape(autofit_shape)))
        .with_slide(Slide::new().with_shape(Shape::AutoShape(casing_shape)))
        .with_slide(Slide::new().with_shape(Shape::AutoShape(field_shape)));

    presentation.save_to_file(&path)?;
    println!("Wrote {}", 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)
}