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 navigation and annotation features: hyperlinks (external and
//! internal, with a bookmark target), comments, footnotes/endnotes, and a
//! structured document tag (content control). One page per feature.
//!
//! Run with: `cargo run -p office-toolkit --example docx_navigation_and_annotations`

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

use office_toolkit::SaveToFile;
use office_toolkit::prelude::*;
use office_toolkit::word::{
    Bookmark, Comment, Hyperlink, Note, NoteReference, Run, StructuredDocumentTag,
};

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

    let document =
        Document::new()
            .with_paragraph(heading(
                "Hyperlinks: external, and internal to a bookmark",
                false,
            ))
            .with_paragraph(
                Paragraph::new()
                    .with_run(Run::new("Visit the ").with_hyperlink(Hyperlink::External(
                        "https://www.rust-lang.org".to_string(),
                    )))
                    .with_run(Run::new("or jump ").with_hyperlink(Hyperlink::External(
                        "https://www.rust-lang.org".to_string(),
                    )))
                    .with_run(
                        Run::new("down to the bookmark")
                            .with_hyperlink(Hyperlink::Internal("Target".to_string())),
                    ),
            )
            .with_paragraph(
                Paragraph::new()
                    .with_run(Run::new("Here is ").with_bookmark(Bookmark::new(0, "Target")))
                    .with_run(
                        Run::new("the bookmarked text.").with_bookmark(Bookmark::new(0, "Target")),
                    ),
            )
            .with_paragraph(heading("Comments", true))
            .with_paragraph(
                Paragraph::new()
                    .with_run(Run::new("This sentence "))
                    .with_run(Run::new("needs a second look").with_comment(0))
                    .with_run(Run::new(" before it ships.")),
            )
            .with_comment(
                Comment::with_text(0, "Please double-check this claim.")
                    .with_author("Reviewer")
                    .with_initials("RV"),
            )
            .with_paragraph(heading("Footnotes and endnotes", true))
            .with_paragraph(
                Paragraph::with_text("A claim that needs a footnote")
                    .with_run(Run::with_note_reference(NoteReference::Footnote(1)))
                    .with_run(Run::new(", and another that needs an endnote"))
                    .with_run(Run::with_note_reference(NoteReference::Endnote(1))),
            )
            .with_footnote(Note::footnote_with_text(
                1,
                "The footnote's own explanatory text.",
            ))
            .with_endnote(Note::endnote_with_text(
                1,
                "The endnote's own explanatory text.",
            ))
            .with_paragraph(heading("A structured document tag (content control)", true))
            .with_paragraph(Paragraph::with_text("Before the content control:"))
            .with_structured_document_tag(
                StructuredDocumentTag::new()
                    .with_id(42)
                    .with_tag("CustomerName")
                    .with_alias("Customer name")
                    .with_paragraph(Paragraph::with_text("Acme Corp.")),
            );

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

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