62 个版本 (29 个破坏性更新)

新功能 0.29.0 2024年8月16日
0.28.1 2024年7月25日
0.27.0 2024年5月30日
0.26.0 2024年3月20日
0.0.2 2022年10月14日

#1101解析器实现

Download history 751/week @ 2024-04-26 498/week @ 2024-05-03 452/week @ 2024-05-10 339/week @ 2024-05-17 374/week @ 2024-05-24 182/week @ 2024-05-31 46/week @ 2024-06-07 55/week @ 2024-06-14 118/week @ 2024-06-21 138/week @ 2024-06-28 215/week @ 2024-07-05 303/week @ 2024-07-12 312/week @ 2024-07-19 270/week @ 2024-07-26 209/week @ 2024-08-02 71/week @ 2024-08-09

985 每月下载量
用于 12 个 crate (11 个直接使用)

MIT/Apache

3.5MB
81K SLoC

write-fonts

此 crate 包含创建和编辑字体文件的类型。


lib.rs:

编写和修改 OpenType 表

此 crate 提供了一组与OpenType 规范中描述的内容相对应的类型,以及将这些类型序列化为二进制字体表的逻辑。它是 read-fonts 的补充,后者提供了对这些类型的有效零分配解析。它旨在用作编译器等字体工程工具的基础。

'write' 与 'read' 类型

Both write-fonts and read-fonts 都大量使用代码生成,并且它们具有类似的结构,其中 tables 模块包含每个支持的 的子模块,该模块包含规范中描述的每个表、记录、标志集或枚举的项目。这意味着存在两个不同的 ValueRecord 类型,一个在 read_fonts::tables::gpos 中定义,另一个在 write_fonts::tables::gpos 中定义。

不同类型的理由在于,这使我们能够极大地简化read-fonts的作用域;在该crate中的类型通常只是原始字节数组切片的视图类型,无法修改,而write-fonts中的类型通常是大家熟悉的Rust结构体和枚举。

字体加载和修改

尽管write-fonts不包含任何解析逻辑,但它提供了FromTableRefToOwnedTable特性(类似于stdFrom & Into),用于将read_fonts类型转换为它的write-fonts等效类型。这意味着您可以将现有的字体表读取到write-fonts中;在底层,我们将使用read-fonts来解析字体,并将其转换为write-fonts版本。通常您不需要自己考虑这种转换;表实现了来自read-fontsFontRead特性,它为您处理了读取 + 转换逻辑。

在加载和修改字体时,您可能需要直接与write-fontsread-fonts交互。为了避免需要管理这两个依赖项,在write-fonts上有一个“读取”特性,它将read-fonts重新导出为crate根目录下的read

# Cargo.toml
[dependencies]
write-fonts = { version = "*", features = ["read"] }
// main.rs
use write_fonts::read::FontRef;

编写子表

字体表通常包含一组子表,这些子表在字体二进制文件中以相对于父表位置(在文件中)的偏移量引用;并且这些子表本身也可以包含子表,依此类推。我们将整个表结构称为“表图”。这种结构的一个后果是,编译表不仅仅是简单地顺序写出每个字段的字节;它还涉及计算子表的顺序,确定它们在最终二进制文件中的位置,并在引用该子表的任何表中正确地写出该位置。

由于大多数子表位置(偏移量)存储为16位整数,在特定情况下,偏移量可能会溢出。为表图中的每个表找到合适的顺序的任务称为“表打包”。write-fonts在序列化时根据HarfBuzz的hb-repacker实现处理表的打包。

示例

创建一个'hhea'表

use write_fonts::{tables::hhea::Hhea, types::{FWord, UfWord}};

let my_table = Hhea {
    ascender: FWord::new(700),
    descender: FWord::new(-195),
    line_gap: FWord::new(0),
    advance_width_max: UfWord::new(1200),
    min_left_side_bearing: FWord::new(-80),
    min_right_side_bearing: FWord::new(-420),
    x_max_extent: FWord::new(1122),
    caret_slope_rise: 1,
    caret_slope_run: 0,
    caret_offset: 0,
    number_of_long_metrics: 301,
};

let _bytes = write_fonts::dump_table(&my_table).expect("failed to write bytes");

读取/修改/写入现有的字体

use read_fonts::{FontRef, TableProvider};
use write_fonts::{
    from_obj::ToOwnedTable,
    tables::head::Head,
    types::LongDateTime,
    FontBuilder,
};
let font_bytes = std::fs::read(path_to_my_font_file).unwrap();
let font = FontRef::new(&font_bytes).expect("failed to read font data");
let mut head: Head = font.head().expect("missing 'head' table").to_owned_table();
head.modified  = seconds_since_font_epoch();
let new_bytes = FontBuilder::new()
    .add_table(&head)
    .unwrap() // errors if we can't compile 'head', unlikely here
    .copy_missing_tables(font)
    .build();
std::fs::write("mynewfont.ttf", &new_bytes).unwrap();

依赖项

~1.7–2.5MB
~48K SLoC