use crate::{
layout::ClassDefSubsetStruct,
offset::{SerializeSerialize, SerializeSubset},
offset_array::SubsetOffsetArray,
serialize::{SerializeErrorFlags, Serializer},
CollectVariationIndices, Plan, Subset, SubsetError, SubsetState, SubsetTable,
};
use fontcull_write_fonts::{
read::{
collections::IntSet,
tables::{
gdef::{
AttachList, AttachPoint, CaretValue, CaretValueFormat1, CaretValueFormat2,
CaretValueFormat3, Gdef, LigCaretList, LigGlyph, MarkGlyphSets,
},
layout::CoverageTable,
},
types::GlyphId,
FontRef, TopLevelTable,
},
types::Offset16,
FontBuilder,
};
impl Subset for Gdef<'_> {
fn subset_with_state(
&self,
plan: &Plan,
_font: &FontRef,
state: &mut SubsetState,
s: &mut Serializer,
_builder: &mut FontBuilder,
) -> Result<(), SubsetError> {
subset_gdef(self, plan, s, state).map_err(|_| SubsetError::SubsetTableError(Gdef::TAG))
}
}
fn subset_gdef(
gdef: &Gdef,
plan: &Plan,
s: &mut Serializer,
state: &mut SubsetState,
) -> Result<(), SerializeErrorFlags> {
let version = gdef.version();
s.embed(version.major)?;
let minor_version_pos = s.embed(version.minor)?;
let glyph_classdef_offset_pos = s.embed(0_u16)?;
let attachlist_offset_pos = s.embed(0_u16)?;
let ligcaret_list_offset_pos = s.embed(0_u16)?;
let markattach_classdef_offset_pos = s.embed(0_u16)?;
let snapshot_version0 = s.snapshot();
let mark_glyphsetsdef_offset_pos = if version.minor >= 2 {
s.embed(0_u16)?
} else {
0
};
let subset_varstore = if version.minor >= 3 {
if let Some(var_store) = gdef
.item_var_store()
.transpose()
.map_err(|_| SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR)?
{
let snapshot_version2 = s.snapshot();
let var_store_offset_pos = s.embed(0_u16)?;
match Offset16::serialize_subset(
&var_store,
s,
plan,
&plan.gdef_varstore_inner_maps,
var_store_offset_pos,
) {
Ok(()) => true,
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => {
s.revert_snapshot(snapshot_version2);
false
}
Err(e) => {
return Err(e);
}
}
} else {
false
}
} else {
false
};
let subset_mark_glyphsets_def = if version.minor >= 2 {
if let Some(mark_glyph_sets_def) = gdef
.mark_glyph_sets_def()
.transpose()
.map_err(|_| SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR)?
{
match Offset16::serialize_subset(
&mark_glyph_sets_def,
s,
plan,
(),
mark_glyphsetsdef_offset_pos,
) {
Ok(()) => true,
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => false,
Err(e) => {
return Err(e);
}
}
} else {
false
}
} else {
false
};
if !subset_varstore {
if subset_mark_glyphsets_def {
s.copy_assign(minor_version_pos, 2_u16);
} else {
s.copy_assign(minor_version_pos, 0_u16);
s.revert_snapshot(snapshot_version0);
}
}
let subset_mark_attach_classdef = if let Some(mark_acttach_classdef) = gdef
.mark_attach_class_def()
.transpose()
.map_err(|_| SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR)?
{
match Offset16::serialize_subset(
&mark_acttach_classdef,
s,
plan,
&ClassDefSubsetStruct {
remap_class: false,
keep_empty_table: false,
use_class_zero: true,
glyph_filter: None,
},
markattach_classdef_offset_pos,
) {
Ok(_) => true,
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => false,
Err(e) => {
return Err(e);
}
}
} else {
false
};
let subset_ligcaret_list = if let Some(ligcaret_list) = gdef
.lig_caret_list()
.transpose()
.map_err(|_| SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR)?
{
match Offset16::serialize_subset(&ligcaret_list, s, plan, (), ligcaret_list_offset_pos) {
Ok(_) => true,
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => false,
Err(e) => {
return Err(e);
}
}
} else {
false
};
let subset_attach_list = if let Some(attach_list) = gdef
.attach_list()
.transpose()
.map_err(|_| SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR)?
{
match Offset16::serialize_subset(&attach_list, s, plan, (), attachlist_offset_pos) {
Ok(_) => true,
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => false,
Err(e) => {
return Err(e);
}
}
} else {
false
};
let subset_glyph_classdef = if let Some(glyph_classdef) = gdef
.glyph_class_def()
.transpose()
.map_err(|_| SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR)?
{
match Offset16::serialize_subset(
&glyph_classdef,
s,
plan,
&ClassDefSubsetStruct {
remap_class: false,
keep_empty_table: false,
use_class_zero: true,
glyph_filter: None,
},
glyph_classdef_offset_pos,
) {
Ok(_) => true,
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => false,
Err(e) => {
return Err(e);
}
}
} else {
false
};
if subset_glyph_classdef
|| subset_attach_list
|| subset_ligcaret_list
|| subset_mark_attach_classdef
|| (version.minor >= 2 && subset_mark_glyphsets_def)
|| (version.minor >= 3 && subset_varstore)
{
state.has_gdef_varstore = subset_varstore;
Ok(())
} else {
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY)
}
}
impl SubsetTable<'_> for AttachList<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
let coverage_offset_pos = s.embed(0_u16)?;
let glyph_count_pos = s.embed(0_u16)?;
let Ok(coverage) = self.coverage() else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
let attach_points = self.attach_points();
let mut count = 0_u16;
let src_glyph_count = self.glyph_count() as usize;
let mut retained_glyphs =
Vec::with_capacity(plan.glyph_map_gsub.len().min(src_glyph_count));
for (idx, glyph) in coverage
.iter()
.enumerate()
.take(plan.font_num_glyphs.min(src_glyph_count))
{
let Some(new_gid) = plan.glyph_map_gsub.get(&GlyphId::from(glyph)) else {
continue;
};
attach_points.subset_offset(idx, s, plan, ())?;
count += 1;
retained_glyphs.push(*new_gid);
}
if retained_glyphs.is_empty() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
s.copy_assign(glyph_count_pos, count);
Offset16::serialize_serialize::<CoverageTable>(s, &retained_glyphs, coverage_offset_pos)
}
}
impl SubsetTable<'_> for AttachPoint<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
_plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
s.embed_bytes(self.min_table_bytes()).map(|_| ())
}
}
impl SubsetTable<'_> for LigCaretList<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
let coverage_offset_pos = s.embed(0_u16)?;
let lig_glyph_count_pos = s.embed(0_u16)?;
let Ok(coverage) = self.coverage() else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
let lig_glyphs = self.lig_glyphs();
let mut count = 0_u16;
let src_lig_glyph_count = self.lig_glyph_count() as usize;
let mut retained_glyphs =
Vec::with_capacity(plan.glyph_map_gsub.len().min(src_lig_glyph_count));
for (idx, glyph) in coverage
.iter()
.enumerate()
.take(plan.font_num_glyphs.min(src_lig_glyph_count))
{
let Some(new_gid) = plan.glyph_map_gsub.get(&GlyphId::from(glyph)) else {
continue;
};
lig_glyphs.subset_offset(idx, s, plan, ())?;
count += 1;
retained_glyphs.push(*new_gid);
}
if retained_glyphs.is_empty() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
s.copy_assign(lig_glyph_count_pos, count);
Offset16::serialize_serialize::<CoverageTable>(s, &retained_glyphs, coverage_offset_pos)
}
}
impl SubsetTable<'_> for LigGlyph<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
let caret_count_pos = s.embed(0_u16)?;
let caret_values = self.caret_values();
let mut count = 0_u16;
for idx in 0..caret_values.len() {
caret_values.subset_offset(idx, s, plan, ())?;
count += 1;
}
if count == 0 {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
s.copy_assign(caret_count_pos, count);
Ok(())
}
}
impl SubsetTable<'_> for MarkGlyphSets<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
s.embed(self.format())?;
let count_pos = s.embed(0_u16)?;
let coverages = self.coverages();
let src_count = self.mark_glyph_set_count() as usize;
let mut count = 0_u16;
for idx in 0..src_count {
match coverages.subset_offset(idx, s, plan, ()) {
Ok(()) => {
count += 1;
}
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => (),
Err(e) => {
return Err(e);
}
}
}
if count == 0 {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
s.copy_assign(count_pos, count);
Ok(())
}
}
impl SubsetTable<'_> for CaretValue<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
match self {
Self::Format1(item) => item.subset(plan, s, args),
Self::Format2(item) => item.subset(plan, s, args),
Self::Format3(item) => item.subset(plan, s, args),
}
}
}
impl SubsetTable<'_> for CaretValueFormat1<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
_plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
s.embed_bytes(self.min_table_bytes()).map(|_| ())
}
}
impl SubsetTable<'_> for CaretValueFormat2<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
_plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
s.embed_bytes(self.min_table_bytes()).map(|_| ())
}
}
impl SubsetTable<'_> for CaretValueFormat3<'_> {
type ArgsForSubset = ();
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
_args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
s.embed(self.caret_value_format())?;
s.embed(self.coordinate())?;
let device_offset_pos = s.embed(0_u16)?;
let Ok(device) = self.device() else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
Offset16::serialize_subset(
&device,
s,
plan,
&plan.layout_varidx_delta_map,
device_offset_pos,
)
}
}
impl CollectVariationIndices for Gdef<'_> {
fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
if let Some(Ok(lig_caret_list)) = self.lig_caret_list() {
lig_caret_list.collect_variation_indices(plan, varidx_set);
}
}
}
impl CollectVariationIndices for LigCaretList<'_> {
fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
let Ok(coverage) = self.coverage() else {
return;
};
let lig_glyphs = self.lig_glyphs();
for (gid, lig_glyph) in coverage.iter().zip(lig_glyphs.iter()) {
let Ok(lig_glyph) = lig_glyph else {
return;
};
if !plan.glyphset_gsub.contains(GlyphId::from(gid)) {
continue;
}
lig_glyph.collect_variation_indices(plan, varidx_set);
}
}
}
impl CollectVariationIndices for LigGlyph<'_> {
fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
for caret in self.caret_values().iter() {
let Ok(caret) = caret else {
return;
};
caret.collect_variation_indices(plan, varidx_set);
}
}
}
impl CollectVariationIndices for CaretValue<'_> {
fn collect_variation_indices(&self, plan: &Plan, varidx_set: &mut IntSet<u32>) {
if let Self::Format3(item) = self {
if let Ok(device) = item.device() {
device.collect_variation_indices(plan, varidx_set);
}
}
}
}
pub(crate) trait CollectUsedMarkSets {
fn collect_used_mark_sets(&self, plan: &Plan, used_mark_sets: &mut IntSet<u16>);
}
impl CollectUsedMarkSets for Gdef<'_> {
fn collect_used_mark_sets(&self, plan: &Plan, used_mark_sets: &mut IntSet<u16>) {
if let Some(Ok(mark_glyph_sets)) = self.mark_glyph_sets_def() {
mark_glyph_sets.collect_used_mark_sets(plan, used_mark_sets);
};
}
}
impl CollectUsedMarkSets for MarkGlyphSets<'_> {
fn collect_used_mark_sets(&self, plan: &Plan, used_mark_sets: &mut IntSet<u16>) {
for (i, coverage) in self.coverages().iter().enumerate() {
let Ok(coverage) = coverage else {
return;
};
if coverage.intersects(&plan.glyphset_gsub) {
used_mark_sets.insert(i as u16);
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use fontcull_write_fonts::{
read::{FontRef, TableProvider},
types::GlyphId,
};
#[test]
fn test_collect_var_indices() {
let mut plan = Plan::default();
plan.glyphset_gsub.insert(GlyphId::from(4_u32));
plan.glyphset_gsub.insert(GlyphId::from(7_u32));
let font =
FontRef::new(include_bytes!("../test-data/fonts/AnekBangla-subset.ttf")).unwrap();
let gdef = font.gdef().unwrap();
let mut varidx_set = IntSet::empty();
gdef.collect_variation_indices(&plan, &mut varidx_set);
assert_eq!(varidx_set.len(), 3);
assert!(varidx_set.contains(3));
assert!(varidx_set.contains(5));
assert!(varidx_set.contains(0));
}
#[test]
fn test_collect_used_mark_sets() {
let mut plan = Plan::default();
plan.glyphset_gsub.insert(GlyphId::from(171_u32));
let font = FontRef::new(include_bytes!("../test-data/fonts/Roboto-Regular.ttf")).unwrap();
let gdef = font.gdef().unwrap();
let mut used_mark_sets = IntSet::empty();
gdef.collect_used_mark_sets(&plan, &mut used_mark_sets);
assert_eq!(used_mark_sets.len(), 1);
assert!(used_mark_sets.contains(0));
}
}