use crate::{
gpos::value_record::{collect_variation_indices, compute_effective_format},
layout::{intersected_coverage_indices, intersected_glyphs_and_indices},
offset::SerializeSerialize,
serialize::{SerializeErrorFlags, Serializer},
CollectVariationIndices, Plan, Serialize, SubsetFlags, SubsetState, SubsetTable,
};
use fnv::FnvHashMap;
use fontcull_write_fonts::{
read::{
collections::IntSet,
tables::{
gpos::{SinglePos, SinglePosFormat1, SinglePosFormat2, ValueFormat, ValueRecord},
layout::CoverageTable,
},
types::GlyphId,
FontData, FontRef, TableProvider,
},
types::Offset16,
};
impl<'a> SubsetTable<'a> for SinglePos<'_> {
type ArgsForSubset = (&'a SubsetState, &'a FontRef<'a>, &'a FnvHashMap<u16, u16>);
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
let args = (args.0, args.1);
match self {
Self::Format1(item) => item.subset(plan, s, args),
Self::Format2(item) => item.subset(plan, s, args),
}
}
}
impl<'a> SubsetTable<'a> for SinglePosFormat1<'_> {
type ArgsForSubset = (&'a SubsetState, &'a FontRef<'a>);
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<(), SerializeErrorFlags> {
let coverage = self
.coverage()
.map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;
let retained_glyphs: Vec<GlyphId> = coverage
.intersect_set(&plan.glyphset_gsub)
.iter()
.filter_map(|g| plan.glyph_map_gsub.get(&g))
.copied()
.collect();
if retained_glyphs.is_empty() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let value_record = self.value_record();
let new_format = if plan
.subset_flags
.contains(SubsetFlags::SUBSET_FLAGS_NO_HINTING)
{
let (state, font) = args;
let strip_hints = if font.fvar().is_ok() {
!state.has_gdef_varstore
} else {
true
};
compute_effective_format(&value_record, strip_hints, true)
} else {
self.value_format()
};
SinglePosFormat1::serialize(
s,
(
&retained_glyphs,
&value_record,
new_format,
plan,
self.offset_data(),
),
)
}
}
impl<'a> Serialize<'a> for SinglePosFormat1<'_> {
type Args = (
&'a [GlyphId],
&'a ValueRecord,
ValueFormat,
&'a Plan,
FontData<'a>,
);
fn serialize(s: &mut Serializer, args: Self::Args) -> Result<(), SerializeErrorFlags> {
s.embed(1_u16)?;
let cov_offset_pos = s.embed(0_u16)?;
let (glyphs, value_record, value_format, plan, font_data) = args;
s.embed(value_format)?;
value_record.subset(plan, s, (value_format, font_data))?;
Offset16::serialize_serialize::<CoverageTable>(s, glyphs, cov_offset_pos)
}
}
fn compute_new_value_format(
plan: &Plan,
has_gdef_varstore: bool,
font: &FontRef,
value_records: impl IntoIterator<Item = ValueRecord>,
) -> ValueFormat {
let mut new_format = ValueFormat::empty();
if plan
.subset_flags
.contains(SubsetFlags::SUBSET_FLAGS_NO_HINTING)
{
let strip_hints = if font.fvar().is_ok() {
!has_gdef_varstore
} else {
true
};
for record in value_records {
new_format |= compute_effective_format(&record, strip_hints, true);
}
} else if let Some(rec) = value_records.into_iter().next() {
new_format = rec.format;
}
new_format
}
impl<'a> SubsetTable<'a> for SinglePosFormat2<'_> {
type ArgsForSubset = (&'a SubsetState, &'a FontRef<'a>);
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<(), SerializeErrorFlags> {
let coverage = self
.coverage()
.map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;
let (retained_glyphs, retained_rec_idxes) =
intersected_glyphs_and_indices(&coverage, &plan.glyphset_gsub, &plan.glyph_map_gsub);
if retained_glyphs.is_empty() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let (state, font) = args;
let value_records = self.value_records();
let it = value_records
.iter()
.enumerate()
.filter(|&(i, ref _rec)| retained_rec_idxes.contains(i as u16))
.filter_map(|(_i, rec)| rec.ok());
let new_format = compute_new_value_format(plan, state.has_gdef_varstore, font, it);
let Ok(first_retained_rec) =
value_records.get(retained_rec_idxes.first().unwrap() as usize)
else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
let mut table_format = 1;
for i in retained_rec_idxes.iter().skip(1) {
let Ok(rec) = value_records.get(i as usize) else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
if rec != first_retained_rec {
table_format = 2;
break;
}
}
if table_format == 1 {
SinglePosFormat1::serialize(
s,
(
&retained_glyphs,
&first_retained_rec,
new_format,
plan,
self.offset_data(),
),
)
} else {
SinglePosFormat2::serialize(
s,
(
&retained_glyphs,
new_format,
self,
&retained_rec_idxes,
plan,
),
)
}
}
}
impl<'a> Serialize<'a> for SinglePosFormat2<'_> {
type Args = (
&'a [GlyphId],
ValueFormat,
&'a SinglePosFormat2<'a>,
&'a IntSet<u16>,
&'a Plan,
);
fn serialize(s: &mut Serializer, args: Self::Args) -> Result<(), SerializeErrorFlags> {
s.embed(2_u16)?;
let cov_offset_pos = s.embed(0_u16)?;
let (glyphs, value_format, table, retained_rec_idxes, plan) = args;
s.embed(value_format)?;
let value_count = glyphs.len();
s.embed(value_count as u16)?;
let value_records = table.value_records();
let font_data = table.offset_data();
for i in retained_rec_idxes.iter() {
let value_record = value_records
.get(i as usize)
.map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;
value_record.subset(plan, s, (value_format, font_data))?;
}
Offset16::serialize_serialize::<CoverageTable>(s, glyphs, cov_offset_pos)
}
}
impl CollectVariationIndices for SinglePos<'_> {
fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
match self {
Self::Format1(item) => item.collect_variation_indices(plan, varidx_set),
Self::Format2(item) => item.collect_variation_indices(plan, varidx_set),
}
}
}
impl CollectVariationIndices for SinglePosFormat1<'_> {
fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
if !self
.value_format()
.intersects(ValueFormat::ANY_DEVICE_OR_VARIDX)
{
return;
}
collect_variation_indices(&self.value_record(), self.offset_data(), plan, varidx_set);
}
}
impl CollectVariationIndices for SinglePosFormat2<'_> {
fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
if !self
.value_format()
.intersects(ValueFormat::ANY_DEVICE_OR_VARIDX)
{
return;
}
let Ok(coverage) = self.coverage() else {
return;
};
let value_records = self.value_records();
let glyph_set = &plan.glyphset_gsub;
let value_record_idxes = intersected_coverage_indices(&coverage, glyph_set);
for i in value_record_idxes.iter() {
let Ok(value_record) = value_records.get(i as usize) else {
return;
};
collect_variation_indices(&value_record, self.offset_data(), plan, varidx_set);
}
}
}
#[cfg(test)]
mod test {
use super::*;
use fontcull_write_fonts::read::{FontRef, TableProvider};
#[test]
fn test_subset_gpos_format1() {
use fontcull_write_fonts::read::tables::gpos::PositionSubtables;
let font = FontRef::new(include_bytes!("../../test-data/fonts/Amiri-Regular.ttf")).unwrap();
let gpos_lookups = font.gpos().unwrap().lookup_list().unwrap();
let lookup = gpos_lookups.lookups().get(6).unwrap();
let PositionSubtables::Single(sub_tables) = lookup.subtables().unwrap() else {
panic!("Wrong type of lookup table!");
};
let singlepos_table = sub_tables.get(0).unwrap();
let subset_state = SubsetState::default();
let mut plan = Plan::default();
plan.glyph_map_gsub
.insert(GlyphId::from(5987_u32), GlyphId::from(3_u32));
plan.glyphset_gsub.insert(GlyphId::from(5987_u32));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
singlepos_table
.subset(&plan, &mut s, (&subset_state, &font, &plan.gpos_lookups))
.unwrap();
assert!(!s.in_error());
s.end_serialize();
let subsetted_data = s.copy_bytes();
let expected_data: [u8; 16] = [
0x00, 0x01, 0x00, 0x0a, 0x00, 0x05, 0xfb, 0xc9, 0xfe, 0xdc, 0x00, 0x01, 0x00, 0x01,
0x00, 0x03,
];
assert_eq!(subsetted_data, expected_data);
}
#[test]
fn test_subset_gpos_format2() {
use fontcull_write_fonts::read::tables::gpos::PositionSubtables;
let font = FontRef::new(include_bytes!("../../test-data/fonts/Amiri-Regular.ttf")).unwrap();
let gpos_lookups = font.gpos().unwrap().lookup_list().unwrap();
let lookup = gpos_lookups.lookups().get(36).unwrap();
let PositionSubtables::Single(sub_tables) = lookup.subtables().unwrap() else {
panic!("Wrong type of lookup table!");
};
let singlepos_table = sub_tables.get(4).unwrap();
let subset_state = SubsetState::default();
let mut plan = Plan::default();
plan.glyph_map_gsub
.insert(GlyphId::from(2270_u32), GlyphId::from(3_u32));
plan.glyph_map_gsub
.insert(GlyphId::from(2349_u32), GlyphId::from(4_u32));
plan.glyphset_gsub.insert(GlyphId::from(2270_u32));
plan.glyphset_gsub.insert(GlyphId::from(2349_u32));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
singlepos_table
.subset(&plan, &mut s, (&subset_state, &font, &plan.gpos_lookups))
.unwrap();
assert!(!s.in_error());
s.end_serialize();
let subsetted_data = s.copy_bytes();
let expected_data: [u8; 20] = [
0x00, 0x02, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x02, 0x00, 0xc3, 0x00, 0xfe, 0x00, 0x01,
0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
];
assert_eq!(subsetted_data, expected_data);
plan.glyph_map_gsub.clear();
plan.glyph_map_gsub
.insert(GlyphId::from(2270_u32), GlyphId::from(3_u32));
plan.glyph_map_gsub
.insert(GlyphId::from(6179_u32), GlyphId::from(4_u32));
plan.glyphset_gsub.clear();
plan.glyphset_gsub.insert(GlyphId::from(2270_u32));
plan.glyphset_gsub.insert(GlyphId::from(6179_u32));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
singlepos_table
.subset(&plan, &mut s, (&subset_state, &font, &plan.gpos_lookups))
.unwrap();
assert!(!s.in_error());
s.end_serialize();
let subsetted_data = s.copy_bytes();
let expected_data: [u8; 16] = [
0x00, 0x01, 0x00, 0x08, 0x00, 0x04, 0x00, 0xc3, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03,
0x00, 0x04,
];
assert_eq!(subsetted_data, expected_data);
}
}