tfm/
serialize.rs

1use super::*;
2
3pub fn serialize(file: &File) -> Vec<u8> {
4    // We leave space at the start of the buffer for the sub file sizes section.
5    // This will be populated at the end after we determine all the section lengths.
6    let mut b = vec![0_u8; 24];
7    let mut sub_file_sizes = SubFileSizes {
8        lf: 0,
9        lh: serialize_header(&file.header, &mut b),
10        bc: match file.char_info_bounds() {
11            None => 1,
12            Some((Char(bc), _)) => bc as i16,
13        },
14        ec: match file.char_info_bounds() {
15            None => 0,
16            Some((bc, ec)) => {
17                serialize_char_infos(file, bc, ec, &mut b);
18                ec.0 as i16
19            }
20        },
21        nw: serialize_section(&file.widths, &mut b, None),
22        nh: serialize_section(&file.heights, &mut b, None),
23        nd: serialize_section(&file.depths, &mut b, None),
24        ni: serialize_section(&file.italic_corrections, &mut b, None),
25        // Is the bug here? we don't serialize the left_boundary_char_entrypoint.
26        // We don't refer to it!! But in the pltoft diff tests it works!
27        nl: serialize_section(
28            &file.lig_kern_program.instructions,
29            &mut b,
30            file.lig_kern_program.right_boundary_char,
31        ),
32        nk: serialize_section(&file.kerns, &mut b, None),
33        ne: serialize_section(&file.extensible_chars, &mut b, None),
34        np: serialize_section(&file.params, &mut b, None),
35    };
36    sub_file_sizes.lf = sub_file_sizes.valid_lf();
37    let sfs_b: [u8; 24] = sub_file_sizes.into();
38    for (i, byte) in sfs_b.into_iter().enumerate() {
39        b[i] = byte;
40    }
41    b
42}
43
44#[derive(Clone)]
45enum SerializableCharTag {
46    None,
47    Valid(CharTag),
48    Unset(u8),
49}
50
51fn serialize_char_infos(file: &File, bc: Char, ec: Char, b: &mut Vec<u8>) {
52    let mut v: Vec<(Option<CharDimensions>, SerializableCharTag)> =
53        vec![(None, SerializableCharTag::None); (ec.0 as usize) + 1 - (bc.0 as usize)];
54    for (c, dimens) in &file.char_dimens {
55        v[(c.0 - bc.0) as usize].0 = Some(dimens.clone());
56    }
57    for (c, tag) in &file.char_tags {
58        if let Some(slot) = v.get_mut((c.0 - bc.0) as usize) {
59            slot.1 = SerializableCharTag::Valid(tag.clone());
60        }
61    }
62    for (c, tag) in &file.unset_char_tags {
63        if let Some(slot) = v.get_mut((c.0 - bc.0) as usize) {
64            slot.1 = SerializableCharTag::Unset(*tag);
65        }
66    }
67    serialize_section(&v, b, None);
68}
69
70fn serialize_section<T: Serializable>(t: &[T], b: &mut Vec<u8>, c: Option<Char>) -> i16 {
71    let start = b.len();
72    for element in t {
73        element.serialize(b, c);
74    }
75    ((b.len() - start) / 4).try_into().unwrap()
76}
77
78trait Serializable: Sized {
79    fn serialize(&self, b: &mut Vec<u8>, _: Option<Char>);
80}
81
82impl Serializable for u32 {
83    fn serialize(&self, b: &mut Vec<u8>, _: Option<Char>) {
84        b.extend(self.to_be_bytes())
85    }
86}
87
88impl Serializable for (Option<CharDimensions>, SerializableCharTag) {
89    fn serialize(&self, b: &mut Vec<u8>, _: Option<Char>) {
90        let italic = match &self.0 {
91            None => {
92                b.extend([0; 2]);
93                0
94            }
95            Some(char_dimens) => {
96                b.push(char_dimens.width_index.get());
97                b.push(
98                    char_dimens
99                        .height_index
100                        .wrapping_mul(16)
101                        .wrapping_add(char_dimens.depth_index),
102                );
103                char_dimens.italic_index
104            }
105        };
106        let (discriminant, payload) = match &self.1 {
107            SerializableCharTag::None => (0_u8, 0_u8),
108            SerializableCharTag::Valid(char_tag) => match char_tag {
109                CharTag::Ligature(p) => (1, *p),
110                CharTag::List(p) => (2, p.0),
111                CharTag::Extension(p) => (3, *p),
112            },
113            SerializableCharTag::Unset(u) => (0, *u),
114        };
115        b.push(italic.wrapping_mul(4).wrapping_add(discriminant));
116        b.push(payload);
117    }
118}
119
120impl Serializable for FixWord {
121    fn serialize(&self, b: &mut Vec<u8>, _: Option<Char>) {
122        (self.0 as u32).serialize(b, None)
123    }
124}
125
126impl Serializable for ligkern::lang::Instruction {
127    fn serialize(&self, b: &mut Vec<u8>, boundary_char: Option<Char>) {
128        // PLtoTF.2014.142
129        let first = [self.next_instruction.unwrap_or(128), self.right_char.0];
130        match self.operation {
131            ligkern::lang::Operation::Kern(_) => {
132                panic!("tfm::format::File lig/kern programs cannot contain `Kern` operations. Use `KernAtIndex` operations instead and provide an appropriate kerns array.");
133            }
134            ligkern::lang::Operation::KernAtIndex(index) => {
135                let [hi, lo] = index.to_be_bytes();
136                b.extend(first);
137                b.push(hi + 128);
138                b.push(lo);
139            }
140            ligkern::lang::Operation::Ligature {
141                char_to_insert,
142                post_lig_operation,
143                post_lig_tag_invalid: _,
144            } => {
145                use ligkern::lang::PostLigOperation::*;
146                b.extend(first);
147                b.push(match post_lig_operation {
148                    RetainBothMoveNowhere => 3,
149                    RetainBothMoveToInserted => 3 + 4,
150                    RetainBothMoveToRight => 3 + 8,
151                    RetainRightMoveToInserted => 1,
152                    RetainRightMoveToRight => 1 + 4,
153                    RetainLeftMoveNowhere => 2,
154                    RetainLeftMoveToInserted => 2 + 4,
155                    RetainNeitherMoveToInserted => 0,
156                });
157                b.push(char_to_insert.0);
158            }
159            ligkern::lang::Operation::EntrypointRedirect(index, char) => {
160                b.extend(match char {
161                    false => [255, 0],
162                    true => match boundary_char {
163                        None => [254, 0],
164                        Some(c) => [255, c.0],
165                    },
166                });
167                b.extend(index.to_be_bytes());
168            }
169        }
170    }
171}
172
173impl Serializable for ExtensibleRecipe {
174    fn serialize(&self, b: &mut Vec<u8>, _: Option<Char>) {
175        b.push(self.top.unwrap_or(Char(0)).0);
176        b.push(self.middle.unwrap_or(Char(0)).0);
177        b.push(self.bottom.unwrap_or(Char(0)).0);
178        b.push(self.rep.0);
179    }
180}
181
182fn serialize_string(s: &Option<String>, size: u8, b: &mut Vec<u8>) {
183    // TODO: issue a warning as in PLtoTF.2014.87 if the string doesn't fit
184    let s = match s {
185        None => "",
186        Some(s) => s,
187    };
188    let len_padding_or = match s.len().try_into() {
189        Ok(len) => size.checked_sub(len).map(|padding| (len, padding)),
190        Err(_) => None,
191    };
192    match len_padding_or {
193        None => {
194            b.push(size);
195            b.extend(&s.as_bytes()[0..size as usize])
196        }
197        Some((len, padding)) => {
198            b.push(len);
199            b.extend(s.as_bytes());
200            b.extend(vec![0; padding as usize]);
201        }
202    }
203}
204
205fn serialize_header(header: &Header, b: &mut Vec<u8>) -> i16 {
206    header.checksum.unwrap_or_default().serialize(b, None);
207    header.design_size.serialize(b, None);
208    serialize_string(&header.character_coding_scheme, 39, b);
209    serialize_string(&header.font_family, 19, b);
210    if header.seven_bit_safe == Some(true) {
211        // Any value >=128 is interpreted as true, but PLtoTF.2014.133 uses 128 exactly...
212        b.push(128);
213    } else {
214        // ...and 0 for false.
215        b.push(0);
216    }
217    b.push(0);
218    b.push(0);
219    b.push(header.face.unwrap_or(0_u8.into()).into());
220    serialize_section(&header.additional_data, b, None);
221    (18 + header.additional_data.len())
222        .try_into()
223        .expect("header.len()=18+header.additional_data.len()<= i16::MAX")
224}