use std::mem::size_of;
use crate::{serialize::Serializer, Plan, Subset, SubsetError, SubsetFlags};
use fontcull_write_fonts::{
read::{tables::gvar::Gvar, types::GlyphId, FontRef, TopLevelTable},
types::Scalar,
FontBuilder,
};
const FIXED_HEADER_SIZE: u32 = 20;
impl Subset for Gvar<'_> {
fn subset(
&self,
plan: &Plan,
_font: &FontRef,
s: &mut Serializer,
_builder: &mut FontBuilder,
) -> Result<(), SubsetError> {
s.embed_bytes(self.offset_data().as_bytes().get(0..12).unwrap())
.map_err(|_| SubsetError::SubsetTableError(Gvar::TAG))?;
let num_glyphs = plan.num_output_glyphs.min(0xFFFF) as u16;
s.embed(num_glyphs)
.map_err(|_| SubsetError::SubsetTableError(Gvar::TAG))?;
let subset_data_size: u32 = plan
.new_to_old_gid_list
.iter()
.filter_map(|x| {
if x.0 == GlyphId::NOTDEF
&& !plan
.subset_flags
.contains(SubsetFlags::SUBSET_FLAGS_NOTDEF_OUTLINE)
{
return None;
}
self.data_for_gid(x.0)
.ok()
.flatten()
.map(|data| data.len() as u32)
})
.sum();
let long_offset = if subset_data_size > 0x1FFFE_u32 {
1_u16
} else {
0_u16
};
s.embed(long_offset)
.map_err(|_| SubsetError::SubsetTableError(Gvar::TAG))?;
if long_offset > 0 {
subset_with_offset_type::<u32>(self, plan, num_glyphs, s)?;
} else {
subset_with_offset_type::<u16>(self, plan, num_glyphs, s)?;
}
Ok(())
}
}
fn subset_with_offset_type<OffsetType: GvarOffset>(
gvar: &Gvar<'_>,
plan: &Plan,
num_glyphs: u16,
s: &mut Serializer,
) -> Result<(), SubsetError> {
let off_size = size_of::<OffsetType>();
let glyph_var_data_offset_array_size = (num_glyphs as u32 + 1) * off_size as u32;
let shared_tuples_offset =
if gvar.shared_tuple_count() == 0 || gvar.shared_tuples_offset().is_null() {
0_u32
} else {
FIXED_HEADER_SIZE + glyph_var_data_offset_array_size
};
s.copy_assign(
gvar.shape().shared_tuples_offset_byte_range().start,
shared_tuples_offset,
);
let shared_tuples_size = 2 * gvar.axis_count() * gvar.shared_tuple_count();
let glyph_var_data_offset =
FIXED_HEADER_SIZE + glyph_var_data_offset_array_size + shared_tuples_size as u32;
s.embed(glyph_var_data_offset)
.map_err(|_| SubsetError::SubsetTableError(Gvar::TAG))?;
let offsets_array_len = (num_glyphs as usize + 1) * off_size;
let mut start_idx = s
.allocate_size(offsets_array_len, false)
.map_err(|_| SubsetError::SubsetTableError(Gvar::TAG))?;
if shared_tuples_offset > 0 {
let offset = gvar.shared_tuples_offset().to_u32() as usize;
let shared_tuples_data = gvar
.offset_data()
.as_bytes()
.get(offset..offset + shared_tuples_size as usize)
.unwrap();
s.embed_bytes(shared_tuples_data)
.map_err(|_| SubsetError::SubsetTableError(Gvar::TAG))?;
}
start_idx += off_size;
let mut glyph_offset = 0_u32;
let mut last = 0;
for (new_gid, old_gid) in plan.new_to_old_gid_list.iter().filter(|x| {
x.0 != GlyphId::NOTDEF
|| plan
.subset_flags
.contains(SubsetFlags::SUBSET_FLAGS_NOTDEF_OUTLINE)
}) {
let last_gid = last;
for _ in last_gid..new_gid.to_u32() {
s.copy_assign(start_idx, OffsetType::stored_value(glyph_offset));
start_idx += off_size;
last += 1;
}
if let Ok(Some(glyph_var_data)) = gvar.data_for_gid(*old_gid) {
s.embed_bytes(glyph_var_data.as_bytes())
.map_err(|_| SubsetError::SubsetTableError(Gvar::TAG))?;
glyph_offset += glyph_var_data.len() as u32;
};
s.copy_assign(start_idx, OffsetType::stored_value(glyph_offset));
start_idx += off_size;
last += 1;
}
for _ in last..plan.num_output_glyphs as u32 {
s.copy_assign(start_idx, OffsetType::stored_value(glyph_offset));
start_idx += off_size;
}
Ok(())
}
trait GvarOffset: Scalar {
fn stored_value(val: u32) -> Self;
}
impl GvarOffset for u16 {
fn stored_value(val: u32) -> u16 {
(val / 2) as u16
}
}
impl GvarOffset for u32 {
fn stored_value(val: u32) -> u32 {
val
}
}