pub mod byte;
pub(crate) mod deserialize;
pub(crate) mod serialize;
pub(crate) mod tables;
use byte::{ByteReader, ByteReaderImpl};
#[cfg(not(feature = "tagging"))]
mod tagging_stub;
#[cfg(feature = "tagging")]
pub(crate) use crate::tagging::*;
#[cfg(not(feature = "tagging"))]
pub(crate) use tagging_stub::*;
use crate::{String, Vec};
use core::marker::PhantomData;
#[repr(u8)]
#[non_exhaustive]
#[derive(Default, Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Version {
#[default]
FV0 = 0b00000000,
}
impl core::fmt::Display for Version {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let version = *self as u8;
write!(f, "FV{:b}", version)
}
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Layout {
pub version: Version,
pub compact: bool,
pub character_tables: Vec<CharacterTable>,
pub color_tables: Vec<ColorTable>,
pub pixmap_tables: Vec<PixmapTable>,
pub font_tables: Vec<FontTable>,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PixmapTable {
pub constant_width: Option<u8>,
pub constant_height: Option<u8>,
pub constant_bits_per_pixel: Option<u8>,
pub color_table_indexes: Option<Vec<u8>>,
pub pixmaps: Vec<Pixmap>,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pixmap {
pub custom_width: Option<u8>,
pub custom_height: Option<u8>,
pub custom_bits_per_pixel: Option<u8>,
pub data: Vec<u8>,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CharacterTable {
pub use_advance_x: bool,
pub use_pixmap_index: bool,
pub use_pixmap_table_index: bool,
pub constant_cluster_codepoints: Option<u8>,
pub pixmap_table_indexes: Option<Vec<u8>>,
pub characters: Vec<Character>,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Character {
pub advance_x: Option<u8>,
pub pixmap_index: Option<u8>,
pub pixmap_table_index: Option<u8>,
pub grapheme_cluster: String,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ColorTable {
pub use_color_type: bool,
pub constant_alpha: Option<u8>,
pub colors: Vec<Color>,
}
#[repr(u8)]
#[non_exhaustive]
#[derive(Default, Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ColorType {
#[default]
Dynamic,
Absolute,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Color {
pub color_type: Option<ColorType>,
pub custom_alpha: Option<u8>,
pub r: u8,
pub g: u8,
pub b: u8,
}
#[repr(u8)]
#[non_exhaustive]
#[derive(Default, Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FontType {
#[default]
Regular,
Bold,
Italic,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FontTable {
pub character_table_indexes: Option<Vec<u8>>,
pub fonts: Vec<Font>,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Font {
pub name: String,
pub author: String,
pub version: u8,
pub font_type: FontType,
pub character_table_indexes: Vec<u8>,
}
#[repr(u8)]
#[rustfmt::skip]
enum TableIdentifier {
Character = 0b00000001,
Pixmap = 0b00000010,
Color = 0b00000011,
Font = 0b00000100,
}
impl TryFrom<u8> for TableIdentifier {
type Error = DeserializeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0b00000001 => Ok(TableIdentifier::Character),
0b00000010 => Ok(TableIdentifier::Pixmap),
0b00000011 => Ok(TableIdentifier::Color),
0b00000100 => Ok(TableIdentifier::Font),
_ => Err(DeserializeError::UnsupportedTableIdentifier),
}
}
}
impl TryFrom<u8> for Version {
type Error = DeserializeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0b00000000 => Ok(Version::FV0),
_ => Err(DeserializeError::UnsupportedVersion),
}
}
}
impl TryFrom<u8> for ColorType {
type Error = DeserializeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(ColorType::Dynamic),
1 => Ok(ColorType::Absolute),
_ => Err(DeserializeError::UnsupportedColorType),
}
}
}
impl TryFrom<u8> for FontType {
type Error = DeserializeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(FontType::Regular),
1 => Ok(FontType::Bold),
2 => Ok(FontType::Italic),
_ => Err(DeserializeError::UnsupportedFontType),
}
}
}
#[derive(Debug)]
pub enum DeserializeError {
UnexpectedEndOfFile,
InvalidSignature,
UnsupportedVersion,
UnsupportedColorType,
UnsupportedTableIdentifier,
UnsupportedFontType,
}
#[derive(Debug)]
pub enum SerializeError {
StaticVectorTooLarge,
InvalidPixmapData,
}
pub(crate) trait Table: Sized {
fn deserialize<R: ByteReader, T: TagWriter>(
engine: &mut DeserializeEngine<R, T>,
) -> Result<Self, DeserializeError>;
fn serialize<T: TagWriter>(
&self,
engine: &mut SerializeEngine<T>,
) -> Result<(), SerializeError>;
}
pub struct DeserializeEngine<'a, R: ByteReader = ByteReaderImpl<'a>, T: TagWriter = TagWriterNoOp> {
bytes: R,
pub layout: Layout,
#[cfg(feature = "tagging")]
pub tags: T,
#[cfg(feature = "tagging")]
tagging_data: TaggingData,
_phantom: PhantomData<T>,
_phantom2: &'a PhantomData<R>,
}
#[derive(Default)]
pub(crate) struct TaggingData {
current_table_index: u8,
current_record_index: u8,
}
pub struct SerializeEngine<'a, T: TagWriter = TagWriterNoOp> {
bytes: byte::ByteWriter,
pub layout: &'a Layout,
#[cfg(feature = "tagging")]
pub tags: T,
#[cfg(feature = "tagging")]
tagging_data: TaggingData,
_phantom: PhantomData<T>,
}
pub(crate) fn deserialize_layout<R: ByteReader, T: TagWriter>(
engine: &mut DeserializeEngine<R, T>,
) -> Result<(), DeserializeError> {
deserialize::next_signature(engine)?;
deserialize::next_version(engine)?;
deserialize::next_header(engine)?;
while engine.bytes.index() < engine.bytes.len() - 1 {
match engine.bytes.next().try_into()? {
TableIdentifier::Character => {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index =
engine.layout.character_tables.len() as u8;
}
let table = CharacterTable::deserialize(engine)?;
engine.layout.character_tables.push(table);
}
TableIdentifier::Pixmap => {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index =
engine.layout.pixmap_tables.len() as u8;
}
let table = PixmapTable::deserialize(engine)?;
engine.layout.pixmap_tables.push(table);
}
TableIdentifier::Color => {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index =
engine.layout.color_tables.len() as u8;
}
let table = ColorTable::deserialize(engine)?;
engine.layout.color_tables.push(table);
}
TableIdentifier::Font => {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index = engine.layout.font_tables.len() as u8;
}
let table = FontTable::deserialize(engine)?;
engine.layout.font_tables.push(table);
}
};
}
Ok(())
}
pub fn deserialize_with_engine<R: ByteReader, T: TagWriter>(
engine: &mut DeserializeEngine<R, T>,
) -> Result<(), DeserializeError> {
deserialize_layout(engine)?;
Ok(())
}
pub fn layout_from_data(buffer: &[u8]) -> Result<Layout, DeserializeError> {
let mut engine = DeserializeEngine::from_data(buffer);
deserialize_with_engine(&mut engine)?;
Ok(engine.layout)
}
pub(crate) fn serialize_layout<T: TagWriter>(
engine: &mut SerializeEngine<T>,
) -> Result<(), SerializeError> {
serialize::push_signature(engine);
serialize::push_version(engine);
serialize::push_header(engine);
for (index, character_table) in engine.layout.character_tables.iter().enumerate() {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index = index as u8;
}
character_table.serialize(engine)?;
}
for (index, pixmap_table) in engine.layout.pixmap_tables.iter().enumerate() {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index = index as u8;
}
pixmap_table.serialize(engine)?;
}
for (index, color_table) in engine.layout.color_tables.iter().enumerate() {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index = index as u8;
}
color_table.serialize(engine)?;
}
for (index, font_table) in engine.layout.font_tables.iter().enumerate() {
#[cfg(feature = "tagging")]
{
engine.tagging_data.current_table_index = index as u8;
}
font_table.serialize(engine)?;
}
Ok(())
}
pub fn serialize_with_engine<T: TagWriter>(
engine: &mut SerializeEngine<T>,
) -> Result<(), SerializeError> {
serialize_layout(engine)?;
Ok(())
}
pub fn layout_to_data(layout: &Layout) -> Result<Vec<u8>, SerializeError> {
let mut engine = SerializeEngine::from_layout(layout);
serialize_with_engine(&mut engine)?;
Ok(engine.data_owned())
}