Skip to main content

rs_docx/
lib.rs

1//! A Rust library for parsing and generating docx files.
2//!
3//! # Create a new document
4//!
5//! Use `Docx::default` to create a new empty `Docx`, then use
6//! [`Docx::write_file`] for saving it to a file.
7//!
8//! [`Docx::write_file`]: struct.Docx.html#method.write_file
9//!
10//! ```no_run
11//! use rs_docx::document::Paragraph;
12//! use rs_docx::Docx;
13//!
14//! let mut docx = Docx::default();
15//!
16//! // create a new paragraph and insert it
17//! let para = Paragraph::default().push_text("Lorem Ipsum");
18//! docx.document.push(para);
19//!
20//! docx.write_file("demo.docx").unwrap();
21//! ```
22//!
23//! Also see: [`Docx::write`].
24//!
25//! [`Docx::write`]: struct.Docx.html#method.write
26//!
27//! # Reading from files
28//!
29//! Use [`DocxFile::from_file`] to extract content from docx files, then use
30//! [`DocxFile::parse`] to generate a `Docx` struct.
31//!
32//! [`DocxFile::from_file`]: struct.DocxFile.html#method.from_file
33//! [`DocxFile::parse`]: struct.DocxFile.html#method.parse
34//!
35//! ```no_run
36//! use rs_docx::document::Paragraph;
37//! use rs_docx::DocxFile;
38//!
39//! let docx = DocxFile::from_file("origin.docx").unwrap();
40//! let mut docx = docx.parse().unwrap();
41//!
42//! let para = Paragraph::default().push_text("Lorem Ipsum");
43//! docx.document.push(para);
44//!
45//! docx.write_file("origin_appended.docx").unwrap();
46//! ```
47//!
48//! To reduce allocations, `DocxFile::parse` returns a `Docx` struct contains
49//! references to `DocxFile` itself. It means you have to make sure that
50//! `DocxFile` lives as long as its returned `Docx`:
51//!
52//! ```compile_fail
53//! use rs_docx::DocxFile;
54//!
55//! let mut docx_option = None;
56//! {
57//!     let docx_file = DocxFile::from_file("foo.docx").unwrap();
58//!     let mut docx = docx_file.parse().unwrap();
59//!     docx_option = Some(docx);
60//!     // `docx_file` gets dropped here and code fails to compile
61//! }
62//! docx_option.unwrap().write_file("foo.docx").unwrap();
63//! ```
64//!
65//! Also see: [`DocxFile::from_reader`].
66//!
67//! [`DocxFile::from_reader`]: struct.DocxFile.html#method.from_reader
68//!
69//! # Similar Projects
70//!
71//! [`bokuweb/docx-rs`]: A .docx file writer with Rust/WebAssembly.
72//!
73//! [`bokuweb/docx-rs`]: https://github.com/bokuweb/docx-rs
74//!
75//! # License
76//!
77//! MIT
78//!
79
80mod macros;
81
82pub mod app;
83pub mod content_type;
84pub mod core;
85pub mod document;
86mod docx;
87mod error;
88pub mod font_table;
89pub mod formatting;
90pub mod media;
91pub mod rels;
92mod schema;
93pub mod settings;
94pub mod styles;
95pub mod web_settings;
96
97use std::io::Write;
98
99use hard_xml::{XmlWrite, XmlWriter};
100
101pub use crate::docx::{Docx, DocxFile};
102pub use crate::error::{DocxError, DocxResult};
103
104pub mod rounded_float {
105    use std::num::ParseFloatError;
106
107    /// Strips common unit suffixes (pt, cm, mm, in, pc, pi, em) from a measurement string.
108    /// Returns the numeric part as a string slice.
109    fn strip_unit_suffix(s: &str) -> &str {
110        let s = s.trim();
111        // Common OOXML/CSS length units
112        const UNITS: &[&str] = &["pt", "cm", "mm", "in", "pc", "pi", "em", "%"];
113        for unit in UNITS {
114            if let Some(stripped) = s.strip_suffix(unit) {
115                return stripped.trim();
116            }
117        }
118        s
119    }
120
121    pub fn from_xml(mode: &str) -> hard_xml::XmlResult<isize> {
122        let numeric_part = strip_unit_suffix(mode);
123        let f: f64 = numeric_part
124            .parse()
125            .map_err(|e: ParseFloatError| hard_xml::XmlError::FromStr(e.into()))?;
126
127        let r = f
128            .is_finite()
129            .then_some(f.round())
130            .ok_or_else(|| hard_xml::XmlError::FromStr("f64 must be finite".into()))?;
131
132        hard_xml::XmlResult::Ok(r as isize)
133    }
134    pub fn to_xml(mode: &isize) -> hard_xml::XmlResult<String> {
135        Ok(mode.to_string())
136    }
137}
138
139pub fn write_attr<W: Write, T: XmlWrite>(
140    element: &Option<T>,
141    writer: &mut XmlWriter<W>,
142) -> Result<(), hard_xml::XmlError> {
143    if let Some(e) = element {
144        e.to_writer(writer)?;
145    };
146    Ok(())
147}