15 个版本
0.3.0 | 2024 年 4 月 28 日 |
---|---|
0.2.2 | 2024 年 4 月 13 日 |
0.2.0 | 2024 年 3 月 12 日 |
0.1.10 | 2024 年 3 月 11 日 |
0.1.1 | 2023 年 10 月 29 日 |
#445 在 解析器实现
211 每月下载量
用于 shiva
74KB
1.5K SLoC
rtf-parser
一个安全的 Rust RTF 解析器和词法分析器库,旨在速度和内存效率,且无外部依赖。
它实现了 RTF 规范的最新版本(1.9),并支持现代 UTF-16 Unicode。
官方文档可在 docs.rs/rtf-parser 查找。
安装
可以使用 cargo 命令行工具安装此库
cargo add rtf-parser
或者在您的 Cargo.toml
中的 [dependencies] 下添加 rtf-parser = "<last-version>"
。
设计
该库分为 2 个主要组件
- 词法分析器
- 解析器
词法分析器扫描文档,并返回一个 Vec<Token>
,以代码可理解的方式表示 RTF 文件。然后将这些标记传递给解析器,将其转换为真实文档:RtfDocument
。
use rtf_parser::lexer::Lexer;
use rtf_parser::tokens::Token;
use rtf_parser::parser::Parser;
use rtf_parser::document::RtfDocument;
fn main() -> Result<(), Box<dyn Error>> {
let tokens: Vec<Token> = Lexer::scan("<rtf>")?;
let parser = Parser::new(tokens);
let doc: RtfDocument = parser.parse()?;
}
或者更简洁的方式
use rtf_parser::document::RtfDocument;
fn main() -> Result<(), Box<dyn Error>> {
let doc: RtfDocument = RtfDocument::try_from("<rtf>")?;
}
RtfDocument
结构实现了 TryFrom
特性,以
&str
String
&mut std::fs::File
和内部处理输入输出的 from_filepath
构造函数。
返回的错误可以是 LexerError
或 ParserError
,具体取决于失败的阶段。
RtfDocument
由以下部分组成
- 包含字体表、颜色表和编码等内容的 头信息。
- 主体,它是一个
Vec<StyledBlock>
。
一个 StyledBlock
包含了特定文本块的所有格式信息。
它包含一个用于文本样式的 Painter
,一个用于布局的 Paragraph
,以及文本(String
)。下面的代码定义了 Painter
,渲染实现由用户决定。
pub struct Painter {
pub font_ref: FontRef,
pub font_size: u16,
pub bold: bool,
pub italic: bool,
pub underline: bool,
pub superscript: bool,
pub subscript: bool,
pub smallcaps: bool,
pub strike: bool,
}
布局信息通过 paragraph
属性公开。
pub struct Paragraph {
pub alignment: Alignment,
pub spacing: Spacing,
pub indent: Indentation,
pub tab_width: i32,
}
它定义了块的对齐方式,使用的间距等...
您还可以使用 RtfDocument
结构体的 to_text
方法提取不带任何格式信息的文本。
fn main() -> Result<(), Box<dyn Error>> {
let rtf = r#"{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard Voici du texte en {\b gras}.\par}"#;
let tokens = Lexer::scan(rtf)?;
let document = Parser::new(tokens)?;
let text = document.to_text();
assert_eq!(text, "Voici du texte en gras.");
}
示例
以下是一个完整的 rtf 解析示例。
use rtf_parser::lexer::Lexer;
use rtf_parser::parser::Parser;
fn main() -> Result<(), Box<dyn Error>> {
let rtf_text = r#"{ \rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard Voici du texte en {\b gras}.\par }"#;
let tokens = Lexer::scan(rtf_text)?;
let doc = Parser::new(tokens).parse()?;
assert_eq!(
doc.header,
RtfHeader {
character_set: Ansi,
color_table: ColorTable::Default(),
font_table: FontTable::from([
(0, Font { name: "Helvetica", character_set: 0, font_family: Swiss })
])
}
);
assert_eq!(
doc.body,
[
StyleBlock {
painter: Painter { font_ref: 0, font_size: 0, bold: false, italic: false, underline: false },
paragraph: Paragraph {
alignment: LeftAligned,
spacing: Spacing { before: 0, after: 0, between_line: Auto, line_multiplier: 0, },
indent: Indentation { left: 0, right: 0, first_line: 0, },
tab_width: 0,
},
text: "Voici du texte en ",
},
StyleBlock {
painter: Painter { font_ref: 0, font_size: 0, bold: true, italic: false, underline: false },
paragraph: Paragraph {
alignment: LeftAligned,
spacing: Spacing { before: 0, after: 0, between_line: Auto, line_multiplier: 0, },
indent: Indentation { left: 0, right: 0, first_line: 0, },
tab_width: 0,
},
text: "gras",
},
StyleBlock {
painter: Painter { font_ref: 0, font_size: 0, bold: false, italic: false, underline: false },
paragraph: Paragraph {
alignment: LeftAligned,
spacing: Spacing { before: 0, after: 0, between_line: Auto, line_multiplier: 0, },
indent: Indentation { left: 0, right: 0, first_line: 0, },
tab_width: 0,
},
text: ".",
},
]
);
return Ok(());
}
已知限制
目前,\bin
关键字没有被考虑。因为其内容是二进制格式的文本,它可能会影响词法分析算法,并导致程序崩溃。未来将很快支持二进制。
同样不支持 base64 图片,但可以安全地解析。
基准测试
目前没有与 rtf-parser
相比较的 crates。
然而,rtf-grimoire
crate 提供了一个相似的 Lexer。以下是针对一个 500kB rtf 文档 的词法分析和解析的快速基准测试。
crates | 版本 | 持续时间 |
---|---|---|
rtf-parser |
v0.3.0 | 7 ms |
rtf-grimoire (仅词法分析) |
v0.2.1 | 13 ms |
此基准测试在一个 Intel MacBook Pro 上以发布版运行.
对于 rtf-parser
,大部分的计算时间(65%)都花在词法分析过程中。仍有很大的改进空间。
依赖关系
~1.5MB
~37K SLoC