Skip to main content

write_fonts/
write.rs

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