#parser #text-format #format #rich #text-parser #rtf

rtf-parser

一个针对速度和内存效率设计的 Rust RTF 解析器和词法分析器库

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解析器实现

Download history 85/week @ 2024-04-29 19/week @ 2024-05-06 27/week @ 2024-05-13 40/week @ 2024-05-20 41/week @ 2024-05-27 31/week @ 2024-06-03 33/week @ 2024-06-10 14/week @ 2024-06-17 13/week @ 2024-06-24 32/week @ 2024-07-01 7/week @ 2024-07-08 10/week @ 2024-07-15 13/week @ 2024-07-22 150/week @ 2024-07-29 27/week @ 2024-08-05 21/week @ 2024-08-12

211 每月下载量
用于 shiva

MIT 许可证

74KB
1.5K SLoC

rtf-parser

Crates.io Crates.io License Crates.io Total Downloads docs.rs

一个安全的 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 个主要组件

  1. 词法分析器
  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 构造函数。

返回的错误可以是 LexerErrorParserError,具体取决于失败的阶段。

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