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 在 解析器实现
985 每月下载量
用于 12 个 crate (11 个直接使用)
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
不包含任何解析逻辑,但它提供了FromTableRef
和ToOwnedTable
特性(类似于std
的From
& Into
),用于将read_fonts
类型转换为它的write-fonts
等效类型。这意味着您可以将现有的字体表读取到write-fonts
中;在底层,我们将使用read-fonts
来解析字体,并将其转换为write-fonts
版本。通常您不需要自己考虑这种转换;表实现了来自read-fonts
的FontRead
特性,它为您处理了读取 + 转换逻辑。
在加载和修改字体时,您可能需要直接与write-fonts
和read-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