use std::collections::BTreeSet;
use crate::error::{Error, PackingError};
use crate::graph::{Graph, ObjectId, ObjectStore, OffsetLen};
use crate::table_type::TableType;
use crate::validate::Validate;
use font_types::Scalar;
pub trait FontWrite {
fn write_into(&self, writer: &mut TableWriter);
fn table_type(&self) -> TableType {
TableType::Unknown
}
}
#[derive(Debug)]
pub struct TableWriter {
tables: ObjectStore,
stack: Vec<TableData>,
offset_adjustment: u32,
}
pub fn dump_table<T: FontWrite + Validate>(table: &T) -> Result<Vec<u8>, Error> {
log::info!("writing table '{}'", table.table_type());
table.validate().map_err(Error::ValidationFailed)?;
let mut graph = TableWriter::make_graph(table);
if !graph.pack_objects() {
return Err(Error::PackingFailed(PackingError {
graph: graph.into(),
}));
}
Ok(graph.serialize())
}
impl TableWriter {
pub(crate) fn make_graph(root: &impl FontWrite) -> Graph {
let mut writer = TableWriter::default();
let root_id = writer.add_table(root);
Graph::from_obj_store(writer.tables, root_id)
}
fn add_table(&mut self, table: &dyn FontWrite) -> ObjectId {
self.stack.push(TableData::default());
table.write_into(self);
let mut table_data = self.stack.pop().unwrap();
table_data.type_ = table.table_type();
self.tables.add(table_data)
}
pub(crate) fn adjust_offsets(&mut self, adjustment: u32, f: impl FnOnce(&mut TableWriter)) {
self.offset_adjustment = adjustment;
f(self);
self.offset_adjustment = 0;
}
#[inline]
pub fn write_slice(&mut self, bytes: &[u8]) {
self.stack.last_mut().unwrap().write_bytes(bytes)
}
pub fn write_offset(&mut self, obj: &dyn FontWrite, width: usize) {
let obj_id = self.add_table(obj);
let data = self.stack.last_mut().unwrap();
data.add_offset(obj_id, width, self.offset_adjustment);
}
pub fn pad_to_2byte_aligned(&mut self) {
if self.stack.last().unwrap().bytes.len() % 2 != 0 {
self.write_slice(&[0]);
}
}
pub(crate) fn into_data(mut self) -> Vec<u8> {
assert_eq!(self.stack.len(), 1);
let result = self.stack.pop().unwrap();
assert!(result.offsets.is_empty());
result.bytes
}
}
impl Default for TableWriter {
fn default() -> Self {
TableWriter {
tables: ObjectStore::default(),
stack: vec![TableData::default()],
offset_adjustment: 0,
}
}
}
#[derive(Debug, Default, Clone)] pub(crate) struct TableData {
pub(crate) type_: TableType,
pub(crate) bytes: Vec<u8>,
pub(crate) offsets: Vec<OffsetRecord>,
}
impl std::hash::Hash for TableData {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
self.offsets.hash(state);
}
}
impl PartialEq for TableData {
fn eq(&self, other: &Self) -> bool {
self.bytes == other.bytes && self.offsets == other.offsets
}
}
impl Eq for TableData {}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub(crate) struct OffsetRecord {
pub(crate) pos: u32,
pub(crate) len: OffsetLen,
pub(crate) object: ObjectId,
pub(crate) adjustment: u32,
}
impl TableData {
pub(crate) fn add_offset(&mut self, object: ObjectId, width: usize, adjustment: u32) {
self.offsets.push(OffsetRecord {
pos: self.bytes.len() as u32,
len: match width {
2 => OffsetLen::Offset16,
3 => OffsetLen::Offset24,
_ => OffsetLen::Offset32,
},
object,
adjustment,
});
let null_bytes = &[0u8, 0, 0, 0].get(..width.min(4)).unwrap();
self.write_bytes(null_bytes);
}
#[allow(dead_code)] pub(crate) fn write<T: Scalar>(&mut self, value: T) {
self.write_bytes(value.to_raw().as_ref())
}
pub(crate) fn write_over<T: Scalar>(&mut self, value: T, pos: usize) {
let raw = value.to_raw();
let len = raw.as_ref().len();
self.bytes[pos..pos + len].copy_from_slice(raw.as_ref());
}
fn write_bytes(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes)
}
#[cfg(test)]
pub fn make_mock(size: usize) -> Self {
TableData {
bytes: vec![0xca; size], offsets: Vec::new(),
type_: TableType::Unknown,
}
}
#[cfg(test)]
pub fn add_mock_offset(&mut self, object: ObjectId, len: OffsetLen) {
let pos = self.offsets.iter().map(|off| off.len as u8 as u32).sum();
self.offsets.push(OffsetRecord {
pos,
len,
object,
adjustment: 0,
});
}
}
macro_rules! write_be_bytes {
($ty:ty) => {
impl FontWrite for $ty {
#[inline]
fn write_into(&self, writer: &mut TableWriter) {
writer.write_slice(&self.to_be_bytes())
}
}
};
}
write_be_bytes!(u8);
write_be_bytes!(i8);
write_be_bytes!(u16);
write_be_bytes!(i16);
write_be_bytes!(u32);
write_be_bytes!(i32);
write_be_bytes!(i64);
write_be_bytes!(types::Uint24);
write_be_bytes!(types::F2Dot14);
write_be_bytes!(types::Fixed);
write_be_bytes!(types::FWord);
write_be_bytes!(types::UfWord);
write_be_bytes!(types::LongDateTime);
write_be_bytes!(types::Tag);
write_be_bytes!(types::Version16Dot16);
write_be_bytes!(types::MajorMinor);
write_be_bytes!(types::GlyphId);
write_be_bytes!(types::NameId);
impl<T: FontWrite> FontWrite for [T] {
fn write_into(&self, writer: &mut TableWriter) {
self.iter().for_each(|item| item.write_into(writer))
}
}
impl<T: FontWrite> FontWrite for BTreeSet<T> {
fn write_into(&self, writer: &mut TableWriter) {
self.iter().for_each(|item| item.write_into(writer))
}
}
impl<T: FontWrite> FontWrite for Vec<T> {
fn write_into(&self, writer: &mut TableWriter) {
self.iter().for_each(|item| item.write_into(writer))
}
}
impl<T: FontWrite> FontWrite for Option<T> {
fn write_into(&self, writer: &mut TableWriter) {
match self {
Some(obj) => obj.write_into(writer),
None => (),
}
}
}