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
//! Word character- and paragraph-level formatting: everything you can put
//! on a run of text (bold/italic/underline/strike, colors, highlight,
//! superscript/subscript, small caps, character spacing) and on a
//! paragraph itself (alignment, tab stops). One page per feature group.
//!
//! Run with: `cargo run -p office-toolkit --example docx_text_formatting`

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

use office_toolkit::SaveToFile;
use office_toolkit::prelude::*;
use office_toolkit::word::{
    Alignment, Highlight, Run, TabLeader, TabStop, TabStopAlignment, UnderlineStyle, VerticalAlign,
};

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

    let document = Document::new()
        .with_paragraph(heading("Docx text formatting — bold, italic, underline, strike", false))
        .with_paragraph(Paragraph::new().with_run(Run::new("Bold text.").with_bold(true)))
        .with_paragraph(Paragraph::new().with_run(Run::new("Italic text.").with_italic(true)))
        .with_paragraph(Paragraph::new().with_run(Run::new("Single underline.").with_underline(UnderlineStyle::Single)))
        .with_paragraph(Paragraph::new().with_run(Run::new("Wavy underline.").with_underline(UnderlineStyle::Wave)))
        .with_paragraph(Paragraph::new().with_run(Run::new("Strikethrough text.").with_strike(true)))
        .with_paragraph(heading("Colors and highlight", true))
        .with_paragraph(Paragraph::new().with_run(Run::new("Red text on the default background.").with_color("FF0000")))
        .with_paragraph(Paragraph::new().with_run(Run::new("Text highlighted in yellow.").with_highlight(Highlight::Yellow)))
        .with_paragraph(
            Paragraph::new()
                .with_run(Run::new("Larger, custom font.").with_font_size(32).with_font_family("Georgia")),
        )
        .with_paragraph(heading("Superscript and subscript", true))
        .with_paragraph(
            Paragraph::new()
                .with_run(Run::new("Water is H"))
                .with_run(Run::new("2").with_vertical_align(VerticalAlign::Subscript))
                .with_run(Run::new("O.")),
        )
        .with_paragraph(
            Paragraph::new()
                .with_run(Run::new("Squared: x"))
                .with_run(Run::new("2").with_vertical_align(VerticalAlign::Superscript))
                .with_run(Run::new(".")),
        )
        .with_paragraph(heading("Small caps, all caps, character spacing", true))
        .with_paragraph(Paragraph::new().with_run(Run::new("rendered in small caps").with_small_caps(true)))
        .with_paragraph(Paragraph::new().with_run(Run::new("rendered in all caps").with_all_caps(true)))
        .with_paragraph(Paragraph::new().with_run(Run::new("Widely spaced letters.").with_character_spacing(120)))
        .with_paragraph(heading("Paragraph alignment", true))
        .with_paragraph(Paragraph::with_text("Left-aligned (the default).").with_alignment(Alignment::Left))
        .with_paragraph(Paragraph::with_text("Centered.").with_alignment(Alignment::Center))
        .with_paragraph(Paragraph::with_text("Right-aligned.").with_alignment(Alignment::Right))
        .with_paragraph(
            Paragraph::with_text(
                "Justified: this line is long enough for Word to stretch the spacing between words so both edges line up.",
            )
            .with_alignment(Alignment::Justify),
        )
        .with_paragraph(heading("Tab stops", true))
        .with_paragraph(
            Paragraph::new()
                .with_tab(TabStop::new(1_440).with_alignment(TabStopAlignment::Center))
                .with_tab(TabStop::new(2_880).with_alignment(TabStopAlignment::Right).with_leader(TabLeader::Dot))
                .with_run(Run::new("Left\tCentered\tRight, dot leader")),
        );

    document.save_to_file(&path)?;
    println!("Wrote {}", path.display());
    Ok(())
}

/// A bold section heading, optionally starting a new page — used to give
/// each feature group its own page in the rendered document.
fn heading(text: &str, new_page: bool) -> Paragraph {
    Paragraph::new()
        .with_run(Run::new(text).with_bold(true).with_font_size(28))
        .with_page_break_before(new_page)
}

/// 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)
}