Files
craft-libs/crates/rtf/src/doc.rs
T
Dylan M. Taylor 7fe902292f craft-rtf: WordCraft's RTF reader and writer on craft-wordmodel and craft-flow (feature doc)
craft-rtf: WordCraft's RTF reader and writer on craft-wordmodel and craft-flow (feature doc)

* Add craft-wordmodel: WordCraft's document model without its editing code

WordCraft's crates/doc (wordcraft-doc, at 07f97737) moved as it is, so the DOCX,
RTF, ODT and other text-format crates can read into and write from one model.
Module paths, type names and serde attributes are unchanged: an app that
re-exports the crate keeps its native file format.

Left in the app: range editing (edit.rs), IME text and equation editing. Not
model, and already in craft-libs: code pages (craft-rtf), the ZIP entry-count
check (craft-opc), the compound-file guard (craft-cfb). The paragraph walks
moved to walk.rs and the unit helpers of wordcraft-geom to geom.rs.

* craft-wordmodel: math_tree, the arguments of an equation node and a walk over its runs

The DOCX reader and writer walk an equation's runs. The walk and the child
accessors it uses were in WordCraft's equation editor (math_edit.rs), which
stays in the app; they are structure, not editing.

* craft-wordmodel: keep the document operations (edit.rs) with the model

The DOCX and RTF readers use them: alternative-format chunks are merged into
a document with adopt_blocks, and a reader formats ranges with format_range
and inserts objects. They operate on the document, not on a view, so they
belong beside it. The tests that drove them come back with them; the
paragraph walks stay in walk.rs and edit re-exports them.

base64 0.23, as WordCraft and craft-offcrypto use.

* Add craft-flow: the flow model the text formats map to, on craft-wordmodel

WordCraft's crates/formats/src/model (07f97737) moved as it is: Flow,
FBlock, Para, Inline, Fmt, tables and cells, pictures, to_doc and
from_doc, picture sniffing, colours, base64 and the Dropped report. Only
the paths changed (wordcraft_doc is craft_wordmodel); the report helpers
are public so the format crates can call them; the image re-encoding
helper stays with WordCraft's RTF writer.

* wip: WordCraft's RTF interpreter, as it is

* Take main's side of files outside craft-rtf

* craft-rtf: WordCraft's RTF reader and writer as they are at wordcraft 07f97737

crates/formats/src/rtf.rs and crates/formats/src/rtf/, unchanged and not yet
compiled (no module declares them), so the next commit shows only what the
move changes.

* craft-rtf: WordCraft's RTF reader and writer as craft_rtf::doc (feature doc)

On craft-flow and craft-wordmodel, with this crate's code pages
(craft-codepage), dates and escaping in place of WordCraft's copies, and
craft-metafile's metafile sniffing. A picture in a format RTF cannot carry is
re-encoded as PNG with a bounded decode (100 megapixels, 512 MiB), as
WordCraft's picture decoder bounded it.

The 76 tests moved with it.
2026-11-11 22:44:19 -05:00

61 lines
2.9 KiB
Rust

//! Rich Text Format, read into and written from the word model (`craft_wordmodel::Document`)
//! through the text formats' flow (`craft_flow`). Feature `doc`.
//!
//! WordCraft's RTF reader and writer.
//!
//! Export: font and colour tables, a small stylesheet (headings with outline levels, Title,
//! Quote, Code), list tables (`\listtable` / `\listoverridetable` with `\listtext` labels for
//! older readers), document info, page setup, paragraph formatting (`\pard`, alignment, page
//! break before), character formatting (`\b \i \ul \strike \super \sub \f \fs \cf \chcbpat`),
//! hyperlinks as `HYPERLINK` fields, bookmarks, pictures (`\pngblip` / `\jpegblip`), tables
//! (`\trowd … \cellx … \cell … \row`, header rows, vertical merges, shading), footnotes and
//! endnotes (`{\super\chftn}{\footnote[\ftnalt] …}`) and comments (`{\*\atrfstart n}` …
//! `{\*\atrfend n}` around the commented text, then `{\*\atnid initials}{\*\atnauthor name}`
//! `\chatn{\*\annotation{\*\atnref n}{\*\atndate …}` text `}`).
//!
//! Import: a tolerant reader for groups and destinations (unknown `\*` destinations and objects
//! are skipped; headers, footers, notes and comments are read into stories of their own),
//! `\par \line \tab \page`, `\uN` with `\ucN`
//! fallback skipping, `\'hh` and 8-bit text in the document's code page (`\ansicpg`, `\mac`, `\pc`, `\pca`) or
//! the current font's (`\fcharset`, `\cpg`: Cyrillic, Central European, Greek, Mac OS Roman, the
//! East Asian multibyte pages 932/936/949/950…; Windows-1252 when none is named). A multibyte
//! code page's bytes stream through one decoder, so a lead byte and its trail byte decode
//! together and a run that looks like a UTF-8 BOM is not mistaken for one; the formatting words
//! above, stylesheet-based
//! headings and `\outlinelevel`, lists through `\ls`/`\ilvl`, fields, bookmarks, pictures and
//! tables, footnotes, endnotes (`\footnote` with `\ftnalt`) and comments (author, initials, date,
//! text, the commented range). Group nesting, picture sizes and the number of notes and comments
//! are capped. Not kept: custom note marks, comment replies (`\atnparent`), notes inside
//! headers, footers and other notes, tables inside note and comment texts (flattened).
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use craft_wordmodel::props::VAlign;
use craft_wordmodel::{Align, Block, Blocks, Document, InlineObject, Paragraph, Rgb};
use craft_flow::{self as model, Cell, FBlock, FTable, Flow, Fmt, Inline, Kind, ListInfo, Meta, Para, RevMark, make_img};
use crate::date;
mod export;
mod parse;
mod reader;
mod state;
mod styles;
mod text;
mod word;
pub use date::{dttm_to_iso, iso_to_dttm};
#[cfg(test)]
use export::{Tables, Writer};
pub use export::{esc, export};
pub use parse::{import, parse};
use reader::Reader;
#[cfg(test)]
use state::MAX_SECTIONS;
#[cfg(test)]
use word::hyperlink;
#[cfg(test)]
mod tests;