Skip to main content

spf/core/
mod.rs

1/*
2 * Copyright 2025 SimplePixelFont
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Essential functions and structs used by both the native crate and FFI interface.
18//!
19//! This module provides raw composite structs that aim to reflect the structure of a `SimplePixelFont`
20//! binary file. Additionally it defines the [`layout_to_data`] and [`layout_from_data`] functions that
21//! can be used to convert between the structs and the binary data.
22
23pub mod byte;
24pub(crate) mod deserialize;
25pub(crate) mod serialize;
26pub(crate) mod tables;
27
28use bitflags::bitflags;
29use byte::{ByteReader, ByteReaderImpl};
30
31#[cfg(not(feature = "tagging"))]
32mod tagging_stub;
33
34#[cfg(feature = "tagging")]
35pub(crate) use crate::tagging::*;
36#[cfg(not(feature = "tagging"))]
37pub(crate) use tagging_stub::*;
38
39use crate::{String, Vec};
40use core::marker::PhantomData;
41
42bitflags! {
43    #[non_exhaustive]
44    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
45    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46    /// Bit flags selecting which configuration values are present for every [`Pixmap`] in a [`PixmapTable`].
47    pub struct PixmapTableConfigurationFlags: u8 {
48        #[doc = include_str!("../../res/snippets/pixmap_table/configurations/flag/use_constant_width.md")]
49        const ConstantWidth = 0b00000001;
50        #[doc = include_str!("../../res/snippets/pixmap_table/configurations/flag/use_constant_height.md")]
51        const ConstantHeight = 0b00000010;
52        #[doc = include_str!("../../res/snippets/pixmap_table/configurations/flag/use_constant_bits_per_pixel.md")]
53        const ConstantBitsPerPixel = 0b00000100;
54    }
55
56    #[non_exhaustive]
57    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59    /// Bit flags selecting which other tables a [`PixmapTable`] links to.
60    pub struct PixmapTableLinkFlags: u8 {
61        #[doc = include_str!("../../res/snippets/pixmap_table/links/flag/link_color_tables.md")]
62        const LinkColorTables = 0b00000001;
63    }
64
65    #[non_exhaustive]
66    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68    /// Bit flags selecting which optional fields are present on every [`Character`] record in a [`CharacterTable`].
69    pub struct CharacterTableModifierFlags: u8 {
70        #[doc = include_str!("../../res/snippets/character_table/modifiers/brief/use_advance_x.md")]
71        #[doc = include_str!("../../res/snippets/character_table/modifiers/details/use_advance_x.md")]
72        const UseAdvanceX = 0b00000001;
73        #[doc = include_str!("../../res/snippets/character_table/modifiers/brief/use_pixmap_index.md")]
74        #[doc = include_str!("../../res/snippets/character_table/modifiers/details/use_pixmap_index.md")]
75        const UsePixmapIndex = 0b00000010;
76        #[doc = include_str!("../../res/snippets/character_table/modifiers/brief/use_pixmap_table_index.md")]
77        #[doc = include_str!("../../res/snippets/character_table/modifiers/details/use_pixmap_table_index.md")]
78        const UsePixmapTableIndex = 0b00000100;
79    }
80
81    #[non_exhaustive]
82    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
83    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84    /// Bit flags selecting which other tables a [`CharacterTable`] links to.
85    pub struct CharacterTableLinkFlags: u8 {
86        #[doc = include_str!("../../res/snippets/character_table/links/flag/link_pixmap_tables.md")]
87        const LinkPixmapTables = 0b00000001;
88    }
89
90    #[non_exhaustive]
91    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
92    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93    /// Bit flags selecting which configuration values are present for every [`Character`] in a [`CharacterTable`].
94    pub struct CharacterTableConfigurationFlags: u8 {
95        #[doc = include_str!("../../res/snippets/character_table/configurations/flag/use_constant_code_point_count.md")]
96        const ConstantCodePointCount = 0b00000001;
97    }
98
99    #[non_exhaustive]
100    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
101    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
102    /// Bit flags selecting which optional fields are present on every [`Color`] record in a [`ColorTable`].
103    pub struct ColorTableModifierFlags: u8 {
104        #[doc = include_str!("../../res/snippets/color_table/modifiers/brief/use_color_type.md")]
105        #[doc = include_str!("../../res/snippets/color_table/modifiers/details/use_color_type.md")]
106        const UseColorType = 0b00000001;
107    }
108
109    #[non_exhaustive]
110    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
111    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
112    /// Bit flags selecting which configuration values are present for every [`Color`] in a [`ColorTable`].
113    pub struct ColorTableConfigurationFlags: u8 {
114        #[doc = include_str!("../../res/snippets/color_table/configurations/flag/use_constant_alpha.md")]
115        const ConstantAlpha = 0b00000001;
116    }
117
118    #[non_exhaustive]
119    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
120    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121    /// Bit flags selecting which other tables a [`FontTable`] links to.
122    pub struct FontTableLinkFlags: u8 {
123        #[doc = include_str!("../../res/snippets/font_table/links/flag/link_character_tables.md")]
124        const LinkCharacterTables = 0b00000001;
125    }
126
127    #[non_exhaustive]
128    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
129    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130    #[doc = include_str!("../../res/snippets/data_types/FontType.md")]
131    pub struct FontType: u8 {
132        #[doc = include_str!("../../res/snippets/data_types/FontType/Bold.md")]
133        const Bold = 0b00000001;
134        #[doc = include_str!("../../res/snippets/data_types/FontType/Italic.md")]
135        const Italic = 0b00000010;
136    }
137}
138
139#[repr(u8)]
140#[non_exhaustive]
141#[derive(Default, Debug, Clone, Copy)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
143#[doc = include_str!("../../res/snippets/data_types/Version.md")]
144pub enum Version {
145    #[default]
146    #[doc = include_str!("../../res/snippets/data_types/Version/FV0.md")]
147    FV0 = 0b00000000,
148}
149
150impl core::fmt::Display for Version {
151    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152        let version = *self as u8;
153        write!(f, "FV{:b}", version)
154    }
155}
156
157#[non_exhaustive]
158#[derive(Default, Debug, Clone)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160/// The full, decoded contents of a `.spf` file: its format version, packing mode, and every table it defines.
161pub struct Layout {
162    /// The format version this layout was parsed from, or should be serialized as.
163    pub version: Version,
164
165    /// Whether partial trailing bytes are packed to the bit (`true`) or padded out to a full byte (`false`).
166    pub compact: bool,
167
168    /// The character tables defined in this file.
169    pub character_tables: Vec<CharacterTable>,
170    /// The color tables defined in this file.
171    pub color_tables: Vec<ColorTable>,
172    /// The pixmap tables defined in this file.
173    pub pixmap_tables: Vec<PixmapTable>,
174    /// The font tables defined in this file.
175    pub font_tables: Vec<FontTable>,
176}
177
178#[non_exhaustive]
179#[derive(Default, Debug, Clone)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181#[doc = include_str!("../../res/snippets/pixmap_table/brief.md")]
182pub struct PixmapTable {
183    /// Which configuration values below are present for every pixmap.
184    pub configuration_flags: PixmapTableConfigurationFlags,
185    #[doc = include_str!("../../res/snippets/pixmap_table/configurations/condition/constant_width.md")]
186    #[doc = include_str!("../../res/snippets/pixmap_table/configurations/brief/constant_width.md")]
187    pub constant_width: Option<u8>,
188    #[doc = include_str!("../../res/snippets/pixmap_table/configurations/condition/constant_height.md")]
189    #[doc = include_str!("../../res/snippets/pixmap_table/configurations/brief/constant_height.md")]
190    pub constant_height: Option<u8>,
191    #[doc = include_str!("../../res/snippets/pixmap_table/configurations/condition/constant_bits_per_pixel.md")]
192    #[doc = include_str!("../../res/snippets/pixmap_table/configurations/brief/constant_bits_per_pixel.md")]
193    pub constant_bits_per_pixel: Option<u8>,
194
195    /// Which other tables this table links to.
196    pub link_flags: PixmapTableLinkFlags,
197    #[doc = include_str!("../../res/snippets/pixmap_table/links/condition/color_tables.md")]
198    #[doc = include_str!("../../res/snippets/pixmap_table/links/brief/color_tables.md")]
199    pub color_table_indexes: Option<Vec<u8>>,
200
201    /// The pixmaps stored in this table.
202    pub pixmaps: Vec<Pixmap>,
203}
204
205#[non_exhaustive]
206#[derive(Default, Debug, Clone)]
207#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
208/// A single glyph's pixel data within a [`PixmapTable`].
209pub struct Pixmap {
210    #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/custom_width.md")]
211    #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/custom_width.md")]
212    pub custom_width: Option<u8>,
213    #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/custom_height.md")]
214    #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/custom_height.md")]
215    pub custom_height: Option<u8>,
216    #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/custom_bits_per_pixel.md")]
217    #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/custom_bits_per_pixel.md")]
218    pub custom_bits_per_pixel: Option<u8>,
219    #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/data.md")]
220    #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/data.md")]
221    pub data: Vec<u8>,
222}
223
224#[non_exhaustive]
225#[derive(Default, Debug, Clone)]
226#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
227#[doc = include_str!("../../res/snippets/character_table/brief.md")]
228pub struct CharacterTable {
229    /// Modifier flags for this [`CharacterTable`].
230    pub modifier_flags: CharacterTableModifierFlags,
231
232    /// Configuration flags for this [`CharacterTable`].
233    pub configuration_flags: CharacterTableConfigurationFlags,
234    #[doc = include_str!("../../res/snippets/character_table/configurations/condition/constant_code_point_count.md")]
235    #[doc = include_str!("../../res/snippets/character_table/configurations/brief/constant_code_point_count.md")]
236    pub constant_code_point_count: Option<u8>,
237
238    /// Link flags for this [`CharacterTable`].
239    pub link_flags: CharacterTableLinkFlags,
240    #[doc = include_str!("../../res/snippets/character_table/links/condition/pixmap_tables.md")]
241    #[doc = include_str!("../../res/snippets/character_table/links/brief/pixmap_tables.md")]
242    pub pixmap_table_indexes: Option<Vec<u8>>,
243
244    /// [`Character`]s stored in this [`CharacterTable`].
245    pub characters: Vec<Character>,
246}
247
248#[non_exhaustive]
249#[derive(Default, Debug, Clone)]
250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251/// A single character's mapping to a pixmap within a [`CharacterTable`].
252pub struct Character {
253    #[doc = include_str!("../../res/snippets/character_table/records/condition/advance_x.md")]
254    #[doc = include_str!("../../res/snippets/character_table/records/brief/advance_x.md")]
255    pub advance_x: Option<u8>,
256    #[doc = include_str!("../../res/snippets/character_table/records/condition/pixmap_index.md")]
257    #[doc = include_str!("../../res/snippets/character_table/records/brief/pixmap_index.md")]
258    pub pixmap_index: Option<u8>,
259    #[doc = include_str!("../../res/snippets/character_table/records/condition/pixmap_table_index.md")]
260    #[doc = include_str!("../../res/snippets/character_table/records/brief/pixmap_table_index.md")]
261    pub pixmap_table_index: Option<u8>,
262
263    #[doc = include_str!("../../res/snippets/character_table/records/condition/code_points.md")]
264    #[doc = include_str!("../../res/snippets/character_table/records/brief/code_points.md")]
265    #[doc = "`spf` handles optional null characters automatically."]
266    pub code_points: String,
267}
268
269#[non_exhaustive]
270#[derive(Default, Debug, Clone)]
271#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
272#[doc = include_str!("../../res/snippets/color_table/brief.md")]
273pub struct ColorTable {
274    /// Which optional per-color fields are present.
275    pub modifier_flags: ColorTableModifierFlags,
276
277    /// Which configuration values below are present for every color.
278    pub configuration_flags: ColorTableConfigurationFlags,
279    #[doc = include_str!("../../res/snippets/color_table/configurations/condition/constant_alpha.md")]
280    #[doc = include_str!("../../res/snippets/color_table/configurations/brief/constant_alpha.md")]
281    pub constant_alpha: Option<u8>,
282
283    /// The colors stored in this table.
284    pub colors: Vec<Color>,
285}
286
287#[repr(u8)]
288#[non_exhaustive]
289#[derive(Default, Debug, Clone, Copy)]
290#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
291#[doc = include_str!("../../res/snippets/data_types/ColorType.md")]
292pub enum ColorType {
293    #[default]
294    #[doc = include_str!("../../res/snippets/data_types/ColorType/Dynamic.md")]
295    Dynamic,
296    #[doc = include_str!("../../res/snippets/data_types/ColorType/Absolute.md")]
297    Absolute,
298}
299
300#[non_exhaustive]
301#[derive(Default, Debug, Clone)]
302#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
303/// A single RGBA color value within a [`ColorTable`].
304pub struct Color {
305    #[doc = include_str!("../../res/snippets/color_table/records/condition/color_type.md")]
306    #[doc = include_str!("../../res/snippets/color_table/records/brief/color_type.md")]
307    pub color_type: Option<ColorType>,
308    #[doc = include_str!("../../res/snippets/color_table/records/condition/custom_alpha.md")]
309    #[doc = include_str!("../../res/snippets/color_table/records/brief/custom_alpha.md")]
310    pub custom_alpha: Option<u8>,
311    #[doc = include_str!("../../res/snippets/color_table/records/condition/red.md")]
312    #[doc = include_str!("../../res/snippets/color_table/records/brief/red.md")]
313    pub red: u8,
314    #[doc = include_str!("../../res/snippets/color_table/records/condition/green.md")]
315    #[doc = include_str!("../../res/snippets/color_table/records/brief/green.md")]
316    pub green: u8,
317    #[doc = include_str!("../../res/snippets/color_table/records/condition/blue.md")]
318    #[doc = include_str!("../../res/snippets/color_table/records/brief/blue.md")]
319    pub blue: u8,
320}
321
322#[non_exhaustive]
323#[derive(Default, Debug, Clone)]
324#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
325#[doc = include_str!("../../res/snippets/font_table/brief.md")]
326pub struct FontTable {
327    /// Which other tables this table links to.
328    pub link_flags: FontTableLinkFlags,
329    #[doc = include_str!("../../res/snippets/font_table/links/condition/character_tables.md")]
330    #[doc = include_str!("../../res/snippets/font_table/links/brief/character_tables.md")]
331    pub character_table_indexes: Option<Vec<u8>>,
332
333    /// The fonts stored in this table.
334    pub fonts: Vec<Font>,
335}
336
337#[non_exhaustive]
338#[derive(Default, Debug, Clone)]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340/// A single named font within a [`FontTable`], grouping the [`CharacterTable`]s it uses.
341pub struct Font {
342    #[doc = include_str!("../../res/snippets/font_table/records/condition/name.md")]
343    #[doc = include_str!("../../res/snippets/font_table/records/brief/name.md")]
344    pub name: String,
345    #[doc = include_str!("../../res/snippets/font_table/records/condition/author.md")]
346    #[doc = include_str!("../../res/snippets/font_table/records/brief/author.md")]
347    pub author: String,
348    #[doc = include_str!("../../res/snippets/font_table/records/condition/version.md")]
349    #[doc = include_str!("../../res/snippets/font_table/records/brief/version.md")]
350    pub version: u8,
351    #[doc = include_str!("../../res/snippets/font_table/records/condition/font_type.md")]
352    #[doc = include_str!("../../res/snippets/font_table/records/brief/font_type.md")]
353    pub font_type: FontType,
354    #[doc = include_str!("../../res/snippets/font_table/records/condition/linked_character_table_indexes.md")]
355    #[doc = include_str!("../../res/snippets/font_table/records/brief/linked_character_table_indexes.md")]
356    pub linked_character_table_indexes: Vec<u8>,
357}
358
359#[repr(u8)]
360#[non_exhaustive]
361#[rustfmt::skip]
362enum TableIdentifier {
363    Character = 0b00000001,
364    Pixmap    = 0b00000010,
365    Color     = 0b00000011,
366    Font      = 0b00000100,
367}
368
369impl TryFrom<u8> for TableIdentifier {
370    type Error = DeserializeError;
371
372    fn try_from(value: u8) -> Result<Self, Self::Error> {
373        match value {
374            0b00000001 => Ok(TableIdentifier::Character),
375            0b00000010 => Ok(TableIdentifier::Pixmap),
376            0b00000011 => Ok(TableIdentifier::Color),
377            0b00000100 => Ok(TableIdentifier::Font),
378            _ => Err(DeserializeError::UnsupportedTableIdentifier),
379        }
380    }
381}
382
383impl TryFrom<u8> for Version {
384    type Error = DeserializeError;
385
386    fn try_from(value: u8) -> Result<Self, Self::Error> {
387        match value {
388            0b00000000 => Ok(Version::FV0),
389            _ => Err(DeserializeError::UnsupportedVersion),
390        }
391    }
392}
393
394impl TryFrom<u8> for ColorType {
395    type Error = DeserializeError;
396
397    fn try_from(value: u8) -> Result<Self, Self::Error> {
398        match value {
399            0 => Ok(ColorType::Dynamic),
400            1 => Ok(ColorType::Absolute),
401            _ => Err(DeserializeError::UnsupportedColorType),
402        }
403    }
404}
405
406impl TryFrom<u8> for FontType {
407    type Error = DeserializeError;
408
409    fn try_from(value: u8) -> Result<Self, Self::Error> {
410        FontType::from_bits(value).ok_or(DeserializeError::UnsupportedFontType)
411    }
412}
413
414#[non_exhaustive]
415#[derive(Debug)]
416/// Errors that can occur while parsing a `.spf` byte buffer into a [`Layout`].
417pub enum DeserializeError {
418    #[doc = include_str!("../../res/snippets/errors/unexpected_end_of_file.md")]
419    UnexpectedEndOfFile,
420    #[doc = include_str!("../../res/snippets/errors/invalid_signature.md")]
421    InvalidSignature,
422    #[doc = include_str!("../../res/snippets/errors/unsupported_version.md")]
423    UnsupportedVersion,
424    #[doc = include_str!("../../res/snippets/errors/unsupported_color_type.md")]
425    UnsupportedColorType,
426    #[doc = include_str!("../../res/snippets/errors/unsupported_table_identifier.md")]
427    UnsupportedTableIdentifier,
428    #[doc = include_str!("../../res/snippets/errors/unsupported_font_type.md")]
429    UnsupportedFontType,
430}
431
432#[non_exhaustive]
433#[derive(Debug)]
434/// Errors that can occur while serializing a [`Layout`] into a `.spf` byte buffer.
435pub enum SerializeError {
436    #[doc = include_str!("../../res/snippets/errors/static_vector_too_large.md")]
437    StaticVectorTooLarge,
438    #[doc = include_str!("../../res/snippets/errors/invalid_pixmap_data.md")]
439    InvalidPixmapData,
440}
441
442pub(crate) trait Table: Sized {
443    fn deserialize<R: ByteReader, T: TagWriter>(
444        engine: &mut DeserializeEngine<R, T>,
445    ) -> Result<Self, DeserializeError>;
446    fn serialize<T: TagWriter>(
447        &self,
448        engine: &mut SerializeEngine<T>,
449    ) -> Result<(), SerializeError>;
450}
451
452/// Drives parsing of a `.spf` byte source into a [`Layout`].
453pub struct DeserializeEngine<'a, R: ByteReader = ByteReaderImpl<'a>, T: TagWriter = TagWriterNoOp> {
454    bytes: R,
455    /// The resulting [`Layout`] after reading from `bytes`.
456    pub layout: Layout,
457    #[cfg(feature = "tagging")]
458    /// Collection of tags marking the byte/bit span of every field read, when the `tagging` feature is enabled.
459    pub tags: T,
460    #[cfg(feature = "tagging")]
461    tagging_data: TaggingData,
462    _phantom: PhantomData<T>,
463    _phantom2: &'a PhantomData<R>,
464}
465
466#[non_exhaustive]
467#[derive(Default)]
468pub(crate) struct TaggingData {
469    current_table_index: u8,
470    current_record_index: u8,
471}
472
473/// Drives serialization of a [`Layout`] into `.spf` bytes.
474pub struct SerializeEngine<'a, T: TagWriter = TagWriterNoOp> {
475    bytes: byte::ByteWriter,
476    /// The [`Layout`] being serialized into `bytes`.
477    pub layout: &'a Layout,
478    #[cfg(feature = "tagging")]
479    /// Collection of tags marking the byte/bit span of every field written, when the `tagging` feature is enabled.
480    pub tags: T,
481    #[cfg(feature = "tagging")]
482    tagging_data: TaggingData,
483    _phantom: PhantomData<T>,
484}
485
486pub(crate) fn deserialize_layout<R: ByteReader, T: TagWriter>(
487    engine: &mut DeserializeEngine<R, T>,
488) -> Result<(), DeserializeError> {
489    deserialize::next_signature(engine)?;
490    deserialize::next_version(engine)?;
491    deserialize::next_header(engine)?;
492
493    while engine.bytes.index() < engine.bytes.len() - 1 {
494        match engine.bytes.next().try_into()? {
495            TableIdentifier::Character => {
496                #[cfg(feature = "tagging")]
497                {
498                    engine.tagging_data.current_table_index =
499                        engine.layout.character_tables.len() as u8;
500                }
501                let table = CharacterTable::deserialize(engine)?;
502                engine.layout.character_tables.push(table);
503            }
504            TableIdentifier::Pixmap => {
505                #[cfg(feature = "tagging")]
506                {
507                    engine.tagging_data.current_table_index =
508                        engine.layout.pixmap_tables.len() as u8;
509                }
510                let table = PixmapTable::deserialize(engine)?;
511                engine.layout.pixmap_tables.push(table);
512            }
513            TableIdentifier::Color => {
514                #[cfg(feature = "tagging")]
515                {
516                    engine.tagging_data.current_table_index =
517                        engine.layout.color_tables.len() as u8;
518                }
519                let table = ColorTable::deserialize(engine)?;
520                engine.layout.color_tables.push(table);
521            }
522            TableIdentifier::Font => {
523                #[cfg(feature = "tagging")]
524                {
525                    engine.tagging_data.current_table_index = engine.layout.font_tables.len() as u8;
526                }
527                let table = FontTable::deserialize(engine)?;
528                engine.layout.font_tables.push(table);
529            }
530        };
531    }
532    Ok(())
533}
534
535/// Deserializes into `engine`'s [`Layout`] using an already-constructed [`DeserializeEngine`]. Use [`layout_from_data`] unless you need direct control over the engine (for example, a custom [`ByteReader`] or [`TagWriter`]).
536pub fn deserialize_with_engine<R: ByteReader, T: TagWriter>(
537    engine: &mut DeserializeEngine<R, T>,
538) -> Result<(), DeserializeError> {
539    deserialize_layout(engine)?;
540    Ok(())
541}
542
543/// Parses a [`&[u8]`] into a font [`Layout`]. This function internally creates a [`DeserializeEngine`]
544/// and calls [`deserialize_with_engine`].
545pub fn layout_from_data(buffer: &[u8]) -> Result<Layout, DeserializeError> {
546    let mut engine = DeserializeEngine::from_data(buffer);
547    deserialize_with_engine(&mut engine)?;
548    Ok(engine.layout)
549}
550
551pub(crate) fn serialize_layout<T: TagWriter>(
552    engine: &mut SerializeEngine<T>,
553) -> Result<(), SerializeError> {
554    serialize::push_signature(engine);
555    serialize::push_version(engine);
556    serialize::push_header(engine);
557
558    for (index, character_table) in engine.layout.character_tables.iter().enumerate() {
559        #[cfg(feature = "tagging")]
560        {
561            engine.tagging_data.current_table_index = index as u8;
562        }
563        character_table.serialize(engine)?;
564    }
565    for (index, pixmap_table) in engine.layout.pixmap_tables.iter().enumerate() {
566        #[cfg(feature = "tagging")]
567        {
568            engine.tagging_data.current_table_index = index as u8;
569        }
570        pixmap_table.serialize(engine)?;
571    }
572    for (index, color_table) in engine.layout.color_tables.iter().enumerate() {
573        #[cfg(feature = "tagging")]
574        {
575            engine.tagging_data.current_table_index = index as u8;
576        }
577        color_table.serialize(engine)?;
578    }
579    for (index, font_table) in engine.layout.font_tables.iter().enumerate() {
580        #[cfg(feature = "tagging")]
581        {
582            engine.tagging_data.current_table_index = index as u8;
583        }
584        font_table.serialize(engine)?;
585    }
586
587    Ok(())
588}
589
590/// Serializes `engine`'s [`Layout`] using an already-constructed [`SerializeEngine`]. Use [`layout_to_data`] unless you need direct control over the engine (for example, a custom [`TagWriter`]).
591pub fn serialize_with_engine<T: TagWriter>(
592    engine: &mut SerializeEngine<T>,
593) -> Result<(), SerializeError> {
594    serialize_layout(engine)?;
595    Ok(())
596}
597
598/// Encodes the provided font [`Layout`] into a [`Vec<u8>`]. This function internally creates a
599/// [`SerializeEngine`] and calls [`serialize_with_engine`].
600pub fn layout_to_data(layout: &Layout) -> Result<Vec<u8>, SerializeError> {
601    let mut engine = SerializeEngine::from_layout(layout);
602    serialize_with_engine(&mut engine)?;
603    Ok(engine.data_owned())
604}