scrivener_rtf/lib.rs
1//! # scrivener-rtf
2//!
3//! A pure Rust RTF (Rich Text Format) parser and generator, optimized for
4//! Scrivener workflows.
5//!
6//! ## Features
7//!
8//! - Parse RTF documents into an AST representation
9//! - Generate RTF from the AST
10//! - Full Unicode support (including surrogate pairs for emoji)
11//! - Extract document properties (fonts, colors, styles)
12//!
13//! ## Example
14//!
15//! ```rust
16//! use scrivener_rtf::{parse, parse_str, parse_file};
17//!
18//! // Parse RTF from bytes
19//! let doc = parse(b"{\\rtf1 Hello, World!}").unwrap();
20//!
21//! // Parse RTF from string
22//! let doc = parse_str("{\\rtf1 Hello}").unwrap();
23//!
24//! // Parse RTF from file
25//! // let doc = parse_file("document.rtf").unwrap();
26//! ```
27//!
28//! ## Generated RTF
29//!
30//! The AST can be converted back to RTF:
31//!
32//! ```rust
33//! use scrivener_rtf::{Document, Group, Content, DocumentProperties};
34//!
35//! let doc = Document {
36//! groups: vec![Group {
37//! content: vec![
38//! Content::ControlWord("rtf".into(), Some(1)),
39//! Content::Text("Hello".into()),
40//! ],
41//! is_destination: false,
42//! }],
43//! properties: DocumentProperties::default(),
44//! };
45//! let rtf = doc.to_rtf();
46//! assert!(rtf.starts_with("{\\rtf1"));
47//! ```
48
49pub mod ast;
50pub mod error;
51pub mod token;
52
53mod encoding;
54mod generator;
55mod lexer;
56mod parser;
57mod properties;
58
59pub use ast::{
60 CharacterSet, Color, Content, Document, DocumentProperties, Font, FontFamily, Group, Style,
61 StyleType,
62};
63pub use error::{Result, RtfError};
64pub use token::Token;
65
66/// Parse RTF bytes into a Document.
67///
68/// This is the primary entry point for parsing RTF content.
69/// It handles tokenization, parsing, and hex escape resolution.
70///
71/// # Example
72///
73/// ```rust
74/// let doc = scrivener_rtf::parse(b"{\\rtf1 Hello}").unwrap();
75/// assert!(!doc.groups.is_empty());
76/// ```
77pub fn parse(input: &[u8]) -> Result<Document> {
78 let tokens = lexer::lex(input)?;
79 let mut doc = parser::parse(&tokens)?;
80 encoding::resolve_hex_escapes(&mut doc);
81 Ok(doc)
82}
83
84/// Parse RTF from a string.
85///
86/// Equivalent to `parse(input.as_bytes())`.
87///
88/// # Example
89///
90/// ```rust
91/// let doc = scrivener_rtf::parse_str("{\\rtf1 Hello}").unwrap();
92/// ```
93pub fn parse_str(input: &str) -> Result<Document> {
94 parse(input.as_bytes())
95}
96
97/// Read and parse an RTF file from disk.
98///
99/// # Example
100///
101/// ```rust,no_run
102/// let doc = scrivener_rtf::parse_file("document.rtf").unwrap();
103/// ```
104pub fn parse_file<P: AsRef<std::path::Path>>(path: P) -> Result<Document> {
105 let bytes = std::fs::read(path)?;
106 parse(&bytes)
107}