Skip to main content

incremental_font_transfer/
lib.rs

1//! A client side implementation of the Incremental Font Transfer standard <https://w3c.github.io/IFT/Overview.html#glyph-keyed>
2//!  
3//! More specifically this provides:
4//! - Implementation of parsing and reading incremental font patch mappings:
5//!   <https://w3c.github.io/IFT/Overview.html#font-format-extensions>
6//! - Implementation of parsing and apply incremental font patches:
7//!   <https://w3c.github.io/IFT/Overview.html#font-patch-formats>
8//!
9//! Built on top of the read-fonts crate.
10
11#![cfg_attr(docsrs, feature(doc_cfg))]
12#![forbid(unsafe_code)]
13
14pub mod font_patch;
15pub mod glyph_keyed;
16pub mod patch_group;
17pub mod patchmap;
18pub mod table_keyed;
19pub mod url_templates;
20
21#[cfg(test)]
22mod testdata {
23    use std::{collections::HashMap, iter};
24
25    use font_test_data::bebuffer::BeBuffer;
26    use skrifa::Tag;
27    use write_fonts::{
28        tables::{head::Head, loca::Loca, maxp::Maxp},
29        FontBuilder,
30    };
31
32    pub fn test_font_for_patching_with_loca_mod<F>(
33        short_loca: bool,
34        loca_mod: F,
35        additional_tables: HashMap<Tag, &[u8]>,
36    ) -> Vec<u8>
37    where
38        F: Fn(&mut Vec<u32>),
39    {
40        let mut font_builder = FontBuilder::new();
41
42        for (tag, data) in additional_tables {
43            font_builder.add_raw(tag, data);
44        }
45
46        let maxp = Maxp {
47            num_glyphs: 15,
48            ..Default::default()
49        };
50        font_builder.add_table(&maxp).unwrap();
51
52        let head = Head {
53            index_to_loc_format: if short_loca { 0 } else { 1 },
54            ..Default::default()
55        };
56        font_builder.add_table(&head).unwrap();
57
58        // ## glyf ##
59        // glyphs are padded to even number of bytes since loca format will be short.
60        let glyf = if !short_loca {
61            // Since we want a long loca artificially inflate glyf table to ensure long offsets are needed.
62            BeBuffer::new().extend(iter::repeat_n(0, 140000))
63        } else {
64            BeBuffer::new()
65        };
66
67        let glyf = glyf
68            .push_with_tag(1u8, "gid_0")
69            .extend([2, 3, 4, 5u8, 0u8])
70            .push_with_tag(6u8, "gid_1")
71            .extend([7, 8u8, 0u8])
72            .push_with_tag(9u8, "gid_8")
73            .extend([10, 11, 12u8]);
74
75        // ## loca ##
76        let gid_0 = glyf.offset_for("gid_0") as u32;
77        let gid_1 = glyf.offset_for("gid_1") as u32;
78        let gid_8 = glyf.offset_for("gid_8") as u32;
79        let end = gid_8 + 4;
80
81        let mut loca = vec![
82            gid_0, // gid 0
83            gid_1, // gid 1
84            gid_8, // gid 2
85            gid_8, // gid 3
86            gid_8, // gid 4
87            gid_8, // gid 5
88            gid_8, // gid 6
89            gid_8, // gid 7
90            gid_8, // gid 8
91            end,   // gid 9
92            end,   // gid 10
93            end,   // gid 11
94            end,   // gid 12
95            end,   // gid 13
96            end,   // gid 14
97            end,   // end
98        ];
99
100        loca_mod(&mut loca);
101
102        let loca = Loca::new(loca);
103        font_builder.add_table(&loca).unwrap();
104
105        let glyf: &[u8] = &glyf;
106        font_builder.add_raw(Tag::new(b"glyf"), glyf);
107
108        font_builder.build()
109    }
110
111    pub fn test_font_for_patching() -> Vec<u8> {
112        test_font_for_patching_with_loca_mod(
113            true,
114            |_| {},
115            HashMap::from([(Tag::new(b"IFT "), vec![0, 0, 0, 0].as_slice())]),
116        )
117    }
118}