mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-13 22:12:12 +00:00
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.
50 lines
2.0 KiB
Rust
50 lines
2.0 KiB
Rust
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
|
//! Rich Text Format.
|
|
//!
|
|
//! Three layers, each usable alone:
|
|
//!
|
|
//! - [`Tokens`]: the tokens of a file (groups, control words, `\'hh`, text runs, `\bin` data).
|
|
//! - [`TextDecoder`] and [`codepage`]: text in the file's code pages, `\uN` with its fallback
|
|
//! characters and surrogate pairs. [`esc`] goes the other way. [`dttm_to_iso`],
|
|
//! [`iso_to_dttm`], [`date_words`] and [`iso_date`] are RTF's two date forms.
|
|
//! - [`read`] and [`write`]: a file to and from a small neutral model ([`Document`]):
|
|
//! paragraphs with outline level, alignment, indents and spacing, runs with bold, italic,
|
|
//! underline, strikethrough, superscript and subscript, size, colour and font, and tables.
|
|
//!
|
|
//! A reader that keeps more than the model holds (styles, lists, fields, notes, pictures) is
|
|
//! built on the first two layers.
|
|
//!
|
|
//! ```
|
|
//! use craft_rtf::{Block, read, write};
|
|
//!
|
|
//! let doc = read(br"{\rtf1\ansi\pard\outlinelevel0 Caf\'e9 {\b \u8364?5}\par}")?;
|
|
//! let Some(Block::Para(p)) = doc.blocks.first() else { return Ok(()) };
|
|
//! assert_eq!((p.text().as_str(), p.outline_level), ("Café €5", Some(0)));
|
|
//! assert!(p.runs[1].fmt.bold);
|
|
//! assert_eq!(read(write(&doc, "Helvetica").as_bytes())?, doc);
|
|
//! # Ok::<(), craft_rtf::NotRtf>(())
|
|
//! ```
|
|
|
|
#[cfg(doctest)]
|
|
#[doc = include_str!("../README.md")]
|
|
struct ReadmeExamples;
|
|
|
|
pub mod codepage;
|
|
mod date;
|
|
#[cfg(feature = "doc")]
|
|
pub mod doc;
|
|
mod lex;
|
|
mod model;
|
|
#[cfg(feature = "outlook")]
|
|
pub mod outlook;
|
|
mod read;
|
|
mod text;
|
|
mod write;
|
|
|
|
pub use date::{date_words, dttm_to_iso, iso_date, iso_to_dttm};
|
|
pub use lex::{Token, Tokens, is_rtf};
|
|
pub use model::{Align, Block, COLUMN_BREAK, Cell, CharFmt, Document, LINE_BREAK, PAGE_BREAK, Para, Run, Table, VertAlign};
|
|
pub use read::{MAX_COLORS, MAX_FONT_NAME, MAX_FONTS, MAX_GROUPS, NotRtf, read};
|
|
pub use text::TextDecoder;
|
|
pub use write::{esc, esc_font, push_esc, write};
|