write_fonts/
write.rs

1use std::collections::BTreeSet;
2
3use crate::error::{Error, PackingError};
4use crate::graph::{Graph, ObjectId, ObjectStore, OffsetLen};
5use crate::table_type::TableType;
6use crate::validate::Validate;
7use font_types::{FixedSize, Scalar};
8use read_fonts::{FontData, FontRead, FontReadWithArgs, ReadError};
9
10/// A type that that can be written out as part of a font file.
11///
12/// This both handles writing big-endian bytes as well as describing the
13/// relationship between tables and their subtables.
14pub trait FontWrite {
15    /// Write our data and information about offsets into this [TableWriter].
16    fn write_into(&self, writer: &mut TableWriter);
17
18    /// The type of this table.
19    ///
20    /// This only matters in cases where a table may require additional processing
21    /// after initial compilation, such as with GPOS/GSUB lookups.
22    fn table_type(&self) -> TableType {
23        TableType::Unknown
24    }
25}
26
27/// An object that manages a collection of serialized tables.
28///
29/// This handles deduplicating objects and tracking offsets.
30#[derive(Debug)]
31pub struct TableWriter {
32    /// Finished tables, associated with an ObjectId; duplicate tables share an id.
33    tables: ObjectStore,
34    /// Tables currently being written.
35    ///
36    /// Tables are processed as they are encountered (as subtables)
37    stack: Vec<TableData>,
38    /// An adjustment factor subtracted from written offsets.
39    ///
40    /// This is '0', unless a particular offset is expected to be relative some
41    /// position *other* than the start of the table.
42    ///
43    /// This should only ever be non-zero in the body of a closure passed to
44    /// [adjust_offsets](Self::adjust_offsets)
45    offset_adjustment: u32,
46}
47
48/// Attempt to serialize a table.
49///
50/// Returns an error if the table is malformed or cannot otherwise be serialized,
51/// otherwise it will return the bytes encoding the table.
52pub fn dump_table<T: FontWrite + Validate>(table: &T) -> Result<Vec<u8>, Error> {
53    log::trace!("writing table '{}'", table.table_type());
54    table.validate()?;
55    let mut graph = TableWriter::make_graph(table);
56
57    if !graph.pack_objects() {
58        return Err(Error::PackingFailed(PackingError {
59            graph: graph.into(),
60        }));
61    }
62    Ok(graph.serialize())
63}
64
65impl TableWriter {
66    /// A convenience method for generating a graph with the provided root object.
67    pub(crate) fn make_graph(root: &impl FontWrite) -> Graph {
68        let mut writer = TableWriter::default();
69        let root_id = writer.add_table(root);
70        Graph::from_obj_store(writer.tables, root_id)
71    }
72
73    fn add_table(&mut self, table: &dyn FontWrite) -> ObjectId {
74        self.stack.push(TableData::default());
75        table.write_into(self);
76        let mut table_data = self.stack.pop().unwrap();
77        table_data.type_ = table.table_type();
78        self.tables.add(table_data)
79    }
80
81    /// Call the provided closure, adjusting any written offsets by `adjustment`.
82    pub(crate) fn adjust_offsets(&mut self, adjustment: u32, f: impl FnOnce(&mut TableWriter)) {
83        self.offset_adjustment = adjustment;
84        f(self);
85        self.offset_adjustment = 0;
86    }
87
88    /// Write raw bytes into this table.
89    ///
90    /// The caller is responsible for ensuring bytes are in big-endian order.
91    #[inline]
92    pub fn write_slice(&mut self, bytes: &[u8]) {
93        self.stack.last_mut().unwrap().write_bytes(bytes)
94    }
95
96    /// Create an offset to another table.
97    ///
98    /// The `width` argument is the size in bytes of the offset, e.g. 2 for
99    /// an `Offset16`, and 4 for an `Offset32`.
100    ///
101    /// The provided table will be serialized immediately, and the position
102    /// of the offset within the current table will be recorded. Offsets
103    /// are resolved when the root table object is serialized, at which point
104    /// we overwrite each recorded offset position with the final offset of the
105    /// appropriate table.
106    pub fn write_offset(&mut self, obj: &dyn FontWrite, width: usize) {
107        let obj_id = self.add_table(obj);
108        let data = self.stack.last_mut().unwrap();
109        data.add_offset(obj_id, width, self.offset_adjustment);
110    }
111
112    /// Add a padding byte of necessary to ensure the table length is an even number.
113    ///
114    /// This is necessary for things like the glyph table, which require offsets
115    /// to be aligned on 2-byte boundaries.
116    pub fn pad_to_2byte_aligned(&mut self) {
117        if self.stack.last().unwrap().bytes.len() % 2 != 0 {
118            self.write_slice(&[0]);
119        }
120    }
121
122    /// used when writing top-level font objects, which are done more manually.
123    pub(crate) fn into_data(mut self) -> TableData {
124        assert_eq!(self.stack.len(), 1);
125        let result = self.stack.pop().unwrap();
126        assert!(result.offsets.is_empty());
127        result
128    }
129
130    /// A reference to the current table data.
131    ///
132    /// This is currently only used to figure out the glyph positions when
133    /// compiling the glyf table.
134    pub(crate) fn current_data(&self) -> &TableData {
135        self.stack.last().unwrap() // there is always at least one
136    }
137}
138
139impl Default for TableWriter {
140    fn default() -> Self {
141        TableWriter {
142            tables: ObjectStore::default(),
143            stack: vec![TableData::default()],
144            offset_adjustment: 0,
145        }
146    }
147}
148
149/// The encoded data for a given table, along with info on included offsets
150#[derive(Debug, Default, Clone)] // DO NOT DERIVE MORE TRAITS! we want to ignore name field
151pub(crate) struct TableData {
152    pub(crate) type_: TableType,
153    pub(crate) bytes: Vec<u8>,
154    pub(crate) offsets: Vec<OffsetRecord>,
155}
156
157impl std::hash::Hash for TableData {
158    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
159        self.bytes.hash(state);
160        self.offsets.hash(state);
161    }
162}
163
164impl PartialEq for TableData {
165    fn eq(&self, other: &Self) -> bool {
166        self.bytes == other.bytes && self.offsets == other.offsets
167    }
168}
169
170impl Eq for TableData {}
171
172/// The position and type of an offset, along with the id of the pointed-to entity
173#[derive(Debug, Clone, Hash, PartialEq, Eq)]
174pub(crate) struct OffsetRecord {
175    /// the position of the offset within the parent table
176    pub(crate) pos: u32,
177    /// the offset length in bytes
178    pub(crate) len: OffsetLen,
179    /// The object pointed to by the offset
180    pub(crate) object: ObjectId,
181    /// a value subtracted from the resolved offset before writing.
182    ///
183    /// In general we assume that offsets are relative to the start of the parent
184    /// table, but in some cases this is not true (for instance, offsets to
185    /// strings in the name table are relative to the end of the table.)
186    pub(crate) adjustment: u32,
187}
188
189impl TableData {
190    pub(crate) fn new(type_: TableType) -> Self {
191        TableData {
192            type_,
193            ..Default::default()
194        }
195    }
196
197    /// the 'adjustment' param is used to modify the written position.
198    pub(crate) fn add_offset(&mut self, object: ObjectId, width: usize, adjustment: u32) {
199        const PLACEHOLDER_BYTES: &[u8] = &[0xff; 4];
200        self.offsets.push(OffsetRecord {
201            pos: self.bytes.len() as u32,
202            len: match width {
203                2 => OffsetLen::Offset16,
204                3 => OffsetLen::Offset24,
205                _ => OffsetLen::Offset32,
206            },
207            object,
208            adjustment,
209        });
210
211        // we don't want to use zeros as our placeholder, since we want to
212        // distinguish from a null offset during splitting/promotion.
213        // we write all ones, since maybe it will stand out in debugging
214        let placeholder = PLACEHOLDER_BYTES.get(..width.min(4)).unwrap();
215        self.write_bytes(placeholder);
216    }
217
218    pub(crate) fn write<T: Scalar>(&mut self, value: T) {
219        self.write_bytes(value.to_raw().as_ref())
220    }
221
222    /// Write the value over existing data at the provided position.
223    ///
224    /// Only used in very special cases. The caller is responsible for knowing
225    /// what they are doing.
226    pub(crate) fn write_over<T: Scalar>(&mut self, value: T, pos: usize) {
227        let raw = value.to_raw();
228        let len = raw.as_ref().len();
229        self.bytes[pos..pos + len].copy_from_slice(raw.as_ref());
230    }
231
232    fn write_bytes(&mut self, bytes: &[u8]) {
233        self.bytes.extend_from_slice(bytes)
234    }
235
236    /// A helper function to reparse this table data as some type.
237    ///
238    /// Used internally when modifying the graph after initial compilation,
239    /// such as during table splitting.
240    pub(crate) fn reparse<'a, T: FontRead<'a>>(&'a self) -> Result<T, ReadError> {
241        let data = FontData::new(&self.bytes);
242        T::read(data)
243    }
244
245    // see above
246    pub(crate) fn reparse_with_args<'a, A, T: FontReadWithArgs<'a, Args = A>>(
247        &'a self,
248        args: &A,
249    ) -> Result<T, ReadError> {
250        let data = FontData::new(&self.bytes);
251        T::read_with_args(data, args)
252    }
253
254    /// A helper function to read a value out of this data.
255    pub(crate) fn read_at<T: Scalar>(&self, pos: usize) -> Option<T> {
256        let len = T::RAW_BYTE_LEN;
257        self.bytes.get(pos..pos + len).and_then(T::read)
258    }
259
260    #[cfg(test)]
261    pub fn make_mock(size: usize) -> Self {
262        TableData {
263            bytes: vec![0xca; size], // has no special meaning
264            offsets: Vec::new(),
265            type_: TableType::MockTable,
266        }
267    }
268
269    #[cfg(test)]
270    pub fn add_mock_offset(&mut self, object: ObjectId, len: OffsetLen) {
271        let pos = self.offsets.iter().map(|off| off.len as u8 as u32).sum();
272        self.offsets.push(OffsetRecord {
273            pos,
274            len,
275            object,
276            adjustment: 0,
277        });
278    }
279}
280
281macro_rules! write_be_bytes {
282    ($ty:ty) => {
283        impl FontWrite for $ty {
284            #[inline]
285            fn write_into(&self, writer: &mut TableWriter) {
286                writer.write_slice(&self.to_be_bytes())
287            }
288        }
289    };
290}
291
292//NOTE: not implemented for offsets! it would be too easy to accidentally write them.
293write_be_bytes!(u8);
294write_be_bytes!(i8);
295write_be_bytes!(u16);
296write_be_bytes!(i16);
297write_be_bytes!(u32);
298write_be_bytes!(i32);
299write_be_bytes!(i64);
300write_be_bytes!(types::Uint24);
301write_be_bytes!(types::Int24);
302write_be_bytes!(types::F2Dot14);
303write_be_bytes!(types::Fixed);
304write_be_bytes!(types::FWord);
305write_be_bytes!(types::UfWord);
306write_be_bytes!(types::LongDateTime);
307write_be_bytes!(types::Tag);
308write_be_bytes!(types::Version16Dot16);
309write_be_bytes!(types::MajorMinor);
310write_be_bytes!(types::GlyphId16);
311write_be_bytes!(types::NameId);
312
313impl<T: FontWrite> FontWrite for [T] {
314    fn write_into(&self, writer: &mut TableWriter) {
315        self.iter().for_each(|item| item.write_into(writer))
316    }
317}
318
319impl<T: FontWrite> FontWrite for BTreeSet<T> {
320    fn write_into(&self, writer: &mut TableWriter) {
321        self.iter().for_each(|item| item.write_into(writer))
322    }
323}
324
325impl<T: FontWrite> FontWrite for Vec<T> {
326    fn write_into(&self, writer: &mut TableWriter) {
327        self.iter().for_each(|item| item.write_into(writer))
328    }
329}
330
331impl<T: FontWrite> FontWrite for Option<T> {
332    fn write_into(&self, writer: &mut TableWriter) {
333        if let Some(obj) = self {
334            obj.write_into(writer)
335        }
336    }
337}