pub mod cmap;
pub mod glyf;
pub mod loca;
pub mod os2;
pub mod svg;
use crate::binary::read::{
CheckIndex, ReadArray, ReadArrayCow, ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadScope,
};
use crate::binary::write::{Placeholder, WriteBinary, WriteContext};
use crate::binary::{I16Be, I64Be, U16Be, U32Be};
use crate::error::{ParseError, WriteError};
use crate::size;
use crate::tag;
use alloc::borrow::Cow;
use alloc::boxed::Box;
use core::convert::TryFrom;
pub const CFF_MAGIC: u32 = tag::OTTO;
pub const TTF_MAGIC: u32 = 0x00010000;
pub const TTCF_MAGIC: u32 = tag::TTCF;
type Fixed = u32;
type LongDateTime = i64;
pub trait FontTableProvider {
fn table_data<'a>(&'a self, tag: u32) -> Result<Option<Cow<'a, [u8]>>, ParseError>;
fn has_table<'a>(&'a self, tag: u32) -> bool;
fn read_table_data<'a>(&'a self, tag: u32) -> Result<Cow<'a, [u8]>, ParseError> {
self.table_data(tag)?.ok_or(ParseError::MissingValue)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct F2Dot14(u16);
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum IndexToLocFormat {
Short,
Long,
}
pub struct OpenTypeFont<'a> {
pub scope: ReadScope<'a>,
pub data: OpenTypeData<'a>,
}
pub enum OpenTypeData<'a> {
Single(OffsetTable<'a>),
Collection(TTCHeader<'a>),
}
pub struct TTCHeader<'a> {
pub major_version: u16,
pub minor_version: u16,
pub offset_tables: ReadArray<'a, U32Be>,
}
#[derive(Clone)]
pub struct OffsetTable<'a> {
pub sfnt_version: u32,
pub search_range: u16,
pub entry_selector: u16,
pub range_shift: u16,
pub table_records: ReadArray<'a, TableRecord>,
}
pub struct OffsetTableFontProvider<'a> {
scope: ReadScope<'a>,
offset_table: Cow<'a, OffsetTable<'a>>,
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash)]
pub struct TableRecord {
pub table_tag: u32,
pub checksum: u32,
pub offset: u32,
pub length: u32,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct HeadTable {
pub major_version: u16,
pub minor_version: u16,
pub font_revision: Fixed,
pub check_sum_adjustment: u32,
pub magic_number: u32,
pub flags: u16,
pub units_per_em: u16,
pub created: LongDateTime,
pub modified: LongDateTime,
pub x_min: i16,
pub y_min: i16,
pub x_max: i16,
pub y_max: i16,
pub mac_style: u16,
pub lowest_rec_ppem: u16,
pub font_direction_hint: i16,
pub index_to_loc_format: IndexToLocFormat,
pub glyph_data_format: i16,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct HheaTable {
pub ascender: i16,
pub descender: i16,
pub line_gap: i16,
pub advance_width_max: u16,
pub min_left_side_bearing: i16,
pub min_right_side_bearing: i16,
pub x_max_extent: i16,
pub caret_slope_rise: i16,
pub caret_slope_run: i16,
pub caret_offset: i16,
pub num_h_metrics: u16,
}
#[derive(Debug)]
pub struct HmtxTable<'a> {
pub h_metrics: ReadArrayCow<'a, LongHorMetric>,
pub left_side_bearings: ReadArrayCow<'a, I16Be>,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct LongHorMetric {
pub advance_width: u16,
pub lsb: i16,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct MaxpTable {
pub num_glyphs: u16,
pub version1_sub_table: Option<MaxpVersion1SubTable>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct MaxpVersion1SubTable {
pub max_points: u16,
pub max_contours: u16,
pub max_composite_points: u16,
pub max_composite_contours: u16,
pub max_zones: u16,
pub max_twilight_points: u16,
pub max_storage: u16,
pub max_function_defs: u16,
pub max_instruction_defs: u16,
pub max_stack_elements: u16,
pub max_size_of_instructions: u16,
pub max_component_elements: u16,
pub max_component_depth: u16,
}
pub struct NameTable<'a> {
pub string_storage: ReadScope<'a>,
pub name_records: ReadArray<'a, NameRecord>,
pub opt_langtag_records: Option<ReadArray<'a, LangTagRecord>>,
}
pub struct NameRecord {
pub platform_id: u16,
pub encoding_id: u16,
pub language_id: u16,
pub name_id: u16,
pub length: u16,
pub offset: u16,
}
pub struct LangTagRecord {
pub length: u16,
pub offset: u16,
}
impl<'a> OpenTypeFont<'a> {
pub fn table_provider(
&'a self,
index: usize,
) -> Result<OffsetTableFontProvider<'a>, ParseError> {
match &self.data {
OpenTypeData::Single(offset_table) => Ok(OffsetTableFontProvider {
offset_table: Cow::Borrowed(offset_table),
scope: self.scope.clone(),
}),
OpenTypeData::Collection(ttc) => ttc
.offset_tables
.check_index(index)
.and_then(|()| ttc.offset_tables.read_item(index))
.and_then(|offset| usize::try_from(offset).map_err(ParseError::from))
.and_then(|offset| self.scope.offset(offset).read::<OffsetTable<'_>>())
.map(|offset_table| OffsetTableFontProvider {
offset_table: Cow::Owned(offset_table),
scope: self.scope.clone(),
}),
}
}
}
impl<'a> ReadBinary<'a> for OpenTypeFont<'a> {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let scope = ctxt.scope();
let mut peek = ctxt.clone();
let magic = peek.read_u32be()?;
match magic {
TTF_MAGIC | CFF_MAGIC => {
let offset_table = ctxt.read::<OffsetTable<'_>>()?;
let font = OpenTypeData::Single(offset_table);
Ok(OpenTypeFont { scope, data: font })
}
TTCF_MAGIC => {
let ttc_header = ctxt.read::<TTCHeader<'_>>()?;
let font = OpenTypeData::Collection(ttc_header);
Ok(OpenTypeFont { scope, data: font })
}
_ => Err(ParseError::BadVersion),
}
}
}
impl<'a> ReadBinary<'a> for TTCHeader<'a> {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let ttc_tag = ctxt.read_u32be()?;
match ttc_tag {
TTCF_MAGIC => {
let major_version = ctxt.read_u16be()?;
let minor_version = ctxt.read_u16be()?;
ctxt.check(major_version == 1 || major_version == 2)?;
let num_fonts = usize::try_from(ctxt.read_u32be()?)?;
let offset_tables = ctxt.read_array::<U32Be>(num_fonts)?;
Ok(TTCHeader {
major_version,
minor_version,
offset_tables,
})
}
_ => Err(ParseError::BadVersion),
}
}
}
impl<'a> ReadBinary<'a> for OffsetTable<'a> {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let sfnt_version = ctxt.read_u32be()?;
match sfnt_version {
TTF_MAGIC | CFF_MAGIC => {
let num_tables = ctxt.read_u16be()?;
let search_range = ctxt.read_u16be()?;
let entry_selector = ctxt.read_u16be()?;
let range_shift = ctxt.read_u16be()?;
let table_records = ctxt.read_array::<TableRecord>(usize::from(num_tables))?;
Ok(OffsetTable {
sfnt_version,
search_range,
entry_selector,
range_shift,
table_records,
})
}
_ => Err(ParseError::BadVersion),
}
}
}
impl<'a> FontTableProvider for OffsetTableFontProvider<'a> {
fn table_data<'b>(&'b self, tag: u32) -> Result<Option<Cow<'b, [u8]>>, ParseError> {
self.offset_table
.read_table(&self.scope, tag)
.map(|scope| scope.map(|scope| Cow::Borrowed(scope.data())))
}
fn has_table<'b>(&'b self, tag: u32) -> bool {
self.offset_table.find_table_record(tag).is_some()
}
}
impl<'a> ReadFrom<'a> for TableRecord {
type ReadType = ((U32Be, U32Be), (U32Be, U32Be));
fn from(((table_tag, checksum), (offset, length)): ((u32, u32), (u32, u32))) -> Self {
TableRecord {
table_tag,
checksum,
offset,
length,
}
}
}
impl WriteBinary<&Self> for TableRecord {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, table: &TableRecord) -> Result<(), WriteError> {
U32Be::write(ctxt, table.table_tag)?;
U32Be::write(ctxt, table.checksum)?;
U32Be::write(ctxt, table.offset)?;
U32Be::write(ctxt, table.length)?;
Ok(())
}
}
impl<'a> OffsetTable<'a> {
pub fn find_table_record(&self, tag: u32) -> Option<TableRecord> {
for table_record in &self.table_records {
if table_record.table_tag == tag {
return Some(table_record);
}
}
None
}
pub fn read_table(
&self,
scope: &ReadScope<'a>,
tag: u32,
) -> Result<Option<ReadScope<'a>>, ParseError> {
if let Some(table_record) = self.find_table_record(tag) {
let table = table_record.read_table(&scope)?;
Ok(Some(table))
} else {
Ok(None)
}
}
}
impl TableRecord {
pub const SIZE: usize = 4 * size::U32;
pub fn read_table<'a>(&self, scope: &ReadScope<'a>) -> Result<ReadScope<'a>, ParseError> {
let offset = usize::try_from(self.offset)?;
let length = usize::try_from(self.length)?;
scope.offset_length(offset, length)
}
}
impl<'a> ReadBinary<'a> for HeadTable {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let major_version = ctxt.read::<U16Be>()?;
let minor_version = ctxt.read::<U16Be>()?;
let font_revision = ctxt.read::<U32Be>()?;
let check_sum_adjustment = ctxt.read::<U32Be>()?;
let magic_number = ctxt.read::<U32Be>()?;
ctxt.check(magic_number == 0x5F0F3CF5)?;
let flags = ctxt.read::<U16Be>()?;
let units_per_em = ctxt.read::<U16Be>()?;
let created = ctxt.read::<I64Be>()?;
let modified = ctxt.read::<I64Be>()?;
let x_min = ctxt.read::<I16Be>()?;
let y_min = ctxt.read::<I16Be>()?;
let x_max = ctxt.read::<I16Be>()?;
let y_max = ctxt.read::<I16Be>()?;
let mac_style = ctxt.read::<U16Be>()?;
let lowest_rec_ppem = ctxt.read::<U16Be>()?;
let font_direction_hint = ctxt.read::<I16Be>()?;
let index_to_loc_format = ctxt.read::<IndexToLocFormat>()?;
let glyph_data_format = ctxt.read::<I16Be>()?;
Ok(HeadTable {
major_version,
minor_version,
font_revision,
check_sum_adjustment,
magic_number,
flags,
units_per_em,
created,
modified,
x_min,
y_min,
x_max,
y_max,
mac_style,
lowest_rec_ppem,
font_direction_hint,
index_to_loc_format,
glyph_data_format,
})
}
}
impl<'a> WriteBinary<&Self> for HeadTable {
type Output = Placeholder<U32Be, u32>;
fn write<C: WriteContext>(ctxt: &mut C, table: &HeadTable) -> Result<Self::Output, WriteError> {
U16Be::write(ctxt, table.major_version)?;
U16Be::write(ctxt, table.minor_version)?;
U32Be::write(ctxt, table.font_revision)?;
let check_sum_adjustment = ctxt.placeholder()?;
U32Be::write(ctxt, table.magic_number)?;
U16Be::write(ctxt, table.flags)?;
U16Be::write(ctxt, table.units_per_em)?;
I64Be::write(ctxt, table.created)?;
I64Be::write(ctxt, table.modified)?;
I16Be::write(ctxt, table.x_min)?;
I16Be::write(ctxt, table.y_min)?;
I16Be::write(ctxt, table.x_max)?;
I16Be::write(ctxt, table.y_max)?;
U16Be::write(ctxt, table.mac_style)?;
U16Be::write(ctxt, table.lowest_rec_ppem)?;
I16Be::write(ctxt, table.font_direction_hint)?;
IndexToLocFormat::write(ctxt, table.index_to_loc_format)?;
I16Be::write(ctxt, table.glyph_data_format)?;
Ok(check_sum_adjustment)
}
}
impl HeadTable {
pub fn is_bold(&self) -> bool {
self.mac_style & 1 != 0
}
pub fn is_italic(&self) -> bool {
self.mac_style & 2 != 0
}
}
impl<'a> ReadBinary<'a> for HheaTable {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let major_version = ctxt.read_u16be()?;
let _minor_version = ctxt.read_u16be()?;
ctxt.check(major_version == 1)?;
let ascender = ctxt.read_i16be()?;
let descender = ctxt.read_i16be()?;
let line_gap = ctxt.read_i16be()?;
let advance_width_max = ctxt.read_u16be()?;
let min_left_side_bearing = ctxt.read_i16be()?;
let min_right_side_bearing = ctxt.read_i16be()?;
let x_max_extent = ctxt.read_i16be()?;
let caret_slope_rise = ctxt.read_i16be()?;
let caret_slope_run = ctxt.read_i16be()?;
let caret_offset = ctxt.read_i16be()?;
let _reserved1 = ctxt.read_i16be()?;
let _reserved2 = ctxt.read_i16be()?;
let _reserved3 = ctxt.read_i16be()?;
let _reserved4 = ctxt.read_i16be()?;
let metric_data_format = ctxt.read_i16be()?;
ctxt.check(metric_data_format == 0)?;
let num_h_metrics = ctxt.read_u16be()?;
Ok(HheaTable {
ascender,
descender,
line_gap,
advance_width_max,
min_left_side_bearing,
min_right_side_bearing,
x_max_extent,
caret_slope_rise,
caret_slope_run,
caret_offset,
num_h_metrics,
})
}
}
impl<'a> WriteBinary<&Self> for HheaTable {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, table: &HheaTable) -> Result<(), WriteError> {
U16Be::write(ctxt, 1u16)?;
U16Be::write(ctxt, 0u16)?;
I16Be::write(ctxt, table.ascender)?;
I16Be::write(ctxt, table.descender)?;
I16Be::write(ctxt, table.line_gap)?;
U16Be::write(ctxt, table.advance_width_max)?;
I16Be::write(ctxt, table.min_left_side_bearing)?;
I16Be::write(ctxt, table.min_right_side_bearing)?;
I16Be::write(ctxt, table.x_max_extent)?;
I16Be::write(ctxt, table.caret_slope_rise)?;
I16Be::write(ctxt, table.caret_slope_run)?;
I16Be::write(ctxt, table.caret_offset)?;
I16Be::write(ctxt, 0i16)?;
I16Be::write(ctxt, 0i16)?;
I16Be::write(ctxt, 0i16)?;
I16Be::write(ctxt, 0i16)?;
I16Be::write(ctxt, 0i16)?;
U16Be::write(ctxt, table.num_h_metrics)?;
Ok(())
}
}
impl<'a> ReadBinaryDep<'a> for HmtxTable<'a> {
type Args = (usize, usize);
type HostType = Self;
fn read_dep(
ctxt: &mut ReadCtxt<'a>,
(num_glyphs, num_h_metrics): (usize, usize),
) -> Result<Self, ParseError> {
let h_metrics = ctxt.read_array::<LongHorMetric>(num_h_metrics)?;
let left_side_bearings =
ctxt.read_array::<I16Be>(num_glyphs.saturating_sub(num_h_metrics))?;
Ok(HmtxTable {
h_metrics: ReadArrayCow::Borrowed(h_metrics),
left_side_bearings: ReadArrayCow::Borrowed(left_side_bearings),
})
}
}
impl<'a> WriteBinary<&Self> for HmtxTable<'a> {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, table: &HmtxTable<'a>) -> Result<(), WriteError> {
ReadArrayCow::write(ctxt, &table.h_metrics)?;
ReadArrayCow::write(ctxt, &table.left_side_bearings)?;
Ok(())
}
}
impl<'a> HmtxTable<'a> {
pub fn horizontal_advance(&self, glyph_id: u16, num_h_metrics: u16) -> Result<u16, ParseError> {
let index = if glyph_id < num_h_metrics {
usize::from(glyph_id)
} else {
usize::from(num_h_metrics.checked_sub(1).ok_or(ParseError::BadIndex)?)
};
self.h_metrics
.check_index(index)
.and_then(|_| self.h_metrics.read_item(index))
.map(|long_hor_metric| long_hor_metric.advance_width)
}
}
impl<'a> ReadFrom<'a> for LongHorMetric {
type ReadType = (U16Be, I16Be);
fn from((advance_width, lsb): (u16, i16)) -> Self {
LongHorMetric { advance_width, lsb }
}
}
impl<'a> WriteBinary for LongHorMetric {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, metric: LongHorMetric) -> Result<(), WriteError> {
U16Be::write(ctxt, metric.advance_width)?;
I16Be::write(ctxt, metric.lsb)?;
Ok(())
}
}
impl<'a> ReadBinary<'a> for MaxpTable {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let version = ctxt.read_u32be()?;
let num_glyphs = ctxt.read_u16be()?;
let sub_table = if version == 0x00010000 {
Some(ctxt.read::<MaxpVersion1SubTable>()?)
} else {
None
};
Ok(MaxpTable {
num_glyphs,
version1_sub_table: sub_table,
})
}
}
impl<'a> WriteBinary<&Self> for MaxpTable {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, table: &MaxpTable) -> Result<(), WriteError> {
if let Some(sub_table) = &table.version1_sub_table {
U32Be::write(ctxt, 0x00010000u32)?;
U16Be::write(ctxt, table.num_glyphs)?;
MaxpVersion1SubTable::write(ctxt, sub_table)?;
} else {
U32Be::write(ctxt, 0x00005000u32)?;
U16Be::write(ctxt, table.num_glyphs)?;
}
Ok(())
}
}
impl<'a> ReadBinary<'a> for MaxpVersion1SubTable {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let max_points = ctxt.read_u16be()?;
let max_contours = ctxt.read_u16be()?;
let max_composite_points = ctxt.read_u16be()?;
let max_composite_contours = ctxt.read_u16be()?;
let max_zones = ctxt.read_u16be()?;
let max_twilight_points = ctxt.read_u16be()?;
let max_storage = ctxt.read_u16be()?;
let max_function_defs = ctxt.read_u16be()?;
let max_instruction_defs = ctxt.read_u16be()?;
let max_stack_elements = ctxt.read_u16be()?;
let max_size_of_instructions = ctxt.read_u16be()?;
let max_component_elements = ctxt.read_u16be()?;
let max_component_depth = ctxt.read_u16be()?;
Ok(MaxpVersion1SubTable {
max_points,
max_contours,
max_composite_points,
max_composite_contours,
max_zones,
max_twilight_points,
max_storage,
max_function_defs,
max_instruction_defs,
max_stack_elements,
max_size_of_instructions,
max_component_elements,
max_component_depth,
})
}
}
impl<'a> WriteBinary<&Self> for MaxpVersion1SubTable {
type Output = ();
fn write<C: WriteContext>(
ctxt: &mut C,
table: &MaxpVersion1SubTable,
) -> Result<(), WriteError> {
U16Be::write(ctxt, table.max_points)?;
U16Be::write(ctxt, table.max_contours)?;
U16Be::write(ctxt, table.max_composite_points)?;
U16Be::write(ctxt, table.max_composite_contours)?;
U16Be::write(ctxt, table.max_zones)?;
U16Be::write(ctxt, table.max_twilight_points)?;
U16Be::write(ctxt, table.max_storage)?;
U16Be::write(ctxt, table.max_function_defs)?;
U16Be::write(ctxt, table.max_instruction_defs)?;
U16Be::write(ctxt, table.max_stack_elements)?;
U16Be::write(ctxt, table.max_size_of_instructions)?;
U16Be::write(ctxt, table.max_component_elements)?;
U16Be::write(ctxt, table.max_component_depth)?;
Ok(())
}
}
impl<'a> ReadBinary<'a> for NameTable<'a> {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let scope = ctxt.scope();
let format = ctxt.read_u16be()?;
ctxt.check(format <= 1)?;
let count = usize::from(ctxt.read_u16be()?);
let string_offset = usize::from(ctxt.read_u16be()?);
let string_storage = scope.offset(string_offset);
let name_records = ctxt.read_array::<NameRecord>(count)?;
let opt_langtag_records = if format > 0 {
let langtag_count = usize::from(ctxt.read_u16be()?);
let langtag_records = ctxt.read_array::<LangTagRecord>(langtag_count)?;
Some(langtag_records)
} else {
None
};
Ok(NameTable {
string_storage,
name_records,
opt_langtag_records,
})
}
}
impl<'a> WriteBinary<&Self> for NameTable<'a> {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, name: &NameTable<'a>) -> Result<(), WriteError> {
let format = name.opt_langtag_records.as_ref().map_or(0u16, |_| 1);
U16Be::write(ctxt, format)?;
U16Be::write(ctxt, u16::try_from(name.name_records.len())?)?;
let string_offset = ctxt.placeholder::<U16Be, _>()?;
<&ReadArray<'a, _>>::write(ctxt, &name.name_records)?;
if let Some(lang_tag_records) = &name.opt_langtag_records {
U16Be::write(ctxt, u16::try_from(lang_tag_records.len())?)?;
<&ReadArray<'a, _>>::write(ctxt, lang_tag_records)?;
}
ctxt.write_placeholder(string_offset, u16::try_from(ctxt.bytes_written())?)?;
ctxt.write_bytes(name.string_storage.data())?;
Ok(())
}
}
impl<'a> ReadFrom<'a> for NameRecord {
type ReadType = ((U16Be, U16Be, U16Be), (U16Be, U16Be, U16Be));
fn from(
((platform_id, encoding_id, language_id), (name_id, length, offset)): (
(u16, u16, u16),
(u16, u16, u16),
),
) -> Self {
NameRecord {
platform_id,
encoding_id,
language_id,
name_id,
length,
offset,
}
}
}
impl<'a> WriteBinary for NameRecord {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, record: NameRecord) -> Result<(), WriteError> {
U16Be::write(ctxt, record.platform_id)?;
U16Be::write(ctxt, record.encoding_id)?;
U16Be::write(ctxt, record.language_id)?;
U16Be::write(ctxt, record.name_id)?;
U16Be::write(ctxt, record.length)?;
U16Be::write(ctxt, record.offset)?;
Ok(())
}
}
impl<'a> ReadFrom<'a> for LangTagRecord {
type ReadType = (U16Be, U16Be);
fn from((length, offset): (u16, u16)) -> Self {
LangTagRecord { length, offset }
}
}
impl<'a> WriteBinary for LangTagRecord {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, record: LangTagRecord) -> Result<(), WriteError> {
U16Be::write(ctxt, record.length)?;
U16Be::write(ctxt, record.offset)?;
Ok(())
}
}
impl<'a> ReadFrom<'a> for F2Dot14 {
type ReadType = U16Be;
fn from(value: u16) -> Self {
F2Dot14(value)
}
}
impl WriteBinary for F2Dot14 {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, val: Self) -> Result<(), WriteError> {
U16Be::write(ctxt, val.0)
}
}
impl<'a> ReadBinary<'a> for IndexToLocFormat {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let index_to_loc_format = ctxt.read_i16be()?;
match index_to_loc_format {
0 => Ok(IndexToLocFormat::Short),
1 => Ok(IndexToLocFormat::Long),
_ => Err(ParseError::BadValue),
}
}
}
impl WriteBinary for IndexToLocFormat {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, index_to_loc_format: Self) -> Result<(), WriteError> {
match index_to_loc_format {
IndexToLocFormat::Short => I16Be::write(ctxt, 0i16),
IndexToLocFormat::Long => I16Be::write(ctxt, 1i16),
}
}
}
impl F2Dot14 {
pub fn new(value: u16) -> Self {
F2Dot14(value)
}
}
impl<T: FontTableProvider> FontTableProvider for Box<T> {
fn table_data<'a>(&'a self, tag: u32) -> Result<Option<Cow<'a, [u8]>>, ParseError> {
self.as_ref().table_data(tag)
}
fn has_table<'a>(&'a self, tag: u32) -> bool {
self.as_ref().has_table(tag)
}
}
#[cfg(test)]
mod tests {
use super::{HeadTable, HmtxTable, NameTable};
use crate::binary::read::ReadScope;
use crate::binary::write::{WriteBinary, WriteBuffer, WriteContext};
#[test]
fn test_write_head_table() {
let head_data = include_bytes!("../tests/fonts/opentype/head.bin");
let head = ReadScope::new(head_data).read::<HeadTable>().unwrap();
let checksum_adjustment = head.check_sum_adjustment;
let mut ctxt = WriteBuffer::new();
let placeholder = HeadTable::write(&mut ctxt, &head).unwrap();
ctxt.write_placeholder(placeholder, checksum_adjustment)
.unwrap();
assert_eq!(ctxt.bytes(), &head_data[..]);
}
#[test]
fn test_write_hmtx_table() {
let hmtx_data = include_bytes!("../tests/fonts/opentype/hmtx.bin");
let num_glyphs = 1264;
let num_h_metrics = 1264;
let hmtx = ReadScope::new(hmtx_data)
.read_dep::<HmtxTable<'_>>((num_glyphs, num_h_metrics))
.unwrap();
let mut ctxt = WriteBuffer::new();
HmtxTable::write(&mut ctxt, &hmtx).unwrap();
assert_eq!(ctxt.bytes(), &hmtx_data[..]);
}
#[test]
fn test_write_name_table() {
let name_data = include_bytes!("../tests/fonts/opentype/name.bin");
let name = ReadScope::new(name_data).read::<NameTable<'_>>().unwrap();
let mut ctxt = WriteBuffer::new();
NameTable::write(&mut ctxt, &name).unwrap();
assert_eq!(ctxt.bytes(), &name_data[..]);
}
}