use crate::{
serialize::{OffsetWhence, SerializeErrorFlags, Serializer},
Plan, Subset, SubsetError, SubsetTable,
};
use fontcull_write_fonts::{
read::{
collections::IntSet,
tables::{
bitmap::{
BitmapSize, IndexSubtable, IndexSubtable1, IndexSubtable3, IndexSubtableList,
IndexSubtableRecord,
},
cbdt::Cbdt,
cblc::Cblc,
},
FontRef, TableProvider, TopLevelTable,
},
types::{FixedSize, GlyphId, Offset32},
FontBuilder,
};
impl Subset for Cblc<'_> {
fn subset(
&self,
plan: &Plan,
font: &FontRef,
s: &mut Serializer,
builder: &mut FontBuilder,
) -> Result<(), SubsetError> {
let cbdt = font
.cbdt()
.or(Err(SubsetError::SubsetTableError(Cbdt::TAG)))?;
s.embed(self.major_version())
.map_err(|_| SubsetError::SubsetTableError(Cblc::TAG))?;
s.embed(self.minor_version())
.map_err(|_| SubsetError::SubsetTableError(Cblc::TAG))?;
let mut num_sizes: u32 = 0;
let num_sizes_pos = s
.embed(num_sizes)
.map_err(|_| SubsetError::SubsetTableError(Cblc::TAG))?;
let bitmapsize_records = self.bitmap_sizes();
let bitmapsize_bytes = self
.offset_data()
.as_bytes()
.get(self.shape().bitmap_sizes_byte_range())
.unwrap();
let mut cbdt_out = Vec::with_capacity(cbdt.offset_data().len());
cbdt_out.extend_from_slice(cbdt.min_table_bytes());
for (idx, bitmap_size_table) in bitmapsize_records.iter().enumerate() {
let start = idx * BitmapSize::RAW_BYTE_LEN;
let src_bytes = bitmapsize_bytes
.get(start..start + BitmapSize::RAW_BYTE_LEN)
.unwrap();
match bitmap_size_table.subset(plan, s, (self, &cbdt, src_bytes, &mut cbdt_out)) {
Ok(()) => num_sizes += 1,
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => continue,
Err(_) => {
return Err(SubsetError::SubsetTableError(Cblc::TAG));
}
}
}
s.copy_assign(num_sizes_pos, num_sizes);
builder.add_raw(Cbdt::TAG, cbdt_out);
Ok(())
}
}
impl<'a> SubsetTable<'a> for BitmapSize {
type ArgsForSubset = (&'a Cblc<'a>, &'a Cbdt<'a>, &'a [u8], &'a mut Vec<u8>);
type Output = ();
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<(), SerializeErrorFlags> {
let (cblc, cbdt, src_bytes, cbdt_out) = args;
if self.start_glyph_index() > plan.glyphset.last().unwrap()
|| self.end_glyph_index() < plan.glyphset.first().unwrap()
{
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let snap = s.snapshot();
let offset_pos = s.embed_bytes(src_bytes)?;
let Ok(index_subtable_list) = self.index_subtable_list(cblc.offset_data()) else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
s.push()?;
match index_subtable_list.subset(plan, s, (cbdt, cbdt_out)) {
Ok((min_gid, max_gid, table_list_size, num_subtables)) => {
let Some(obj_idx) = s.pop_pack(true) else {
return Err(s.error());
};
let _ = s.add_link(
offset_pos..offset_pos + Offset32::RAW_BYTE_LEN,
obj_idx,
OffsetWhence::Head,
0,
false,
);
s.copy_assign(offset_pos + 4, table_list_size as u32);
s.copy_assign(offset_pos + 8, num_subtables as u32);
s.copy_assign(offset_pos + 40, min_gid.to_u32() as u16);
s.copy_assign(offset_pos + 42, max_gid.to_u32() as u16);
}
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => {
s.pop_discard();
s.revert_snapshot(snap);
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
Err(e) => {
return Err(e);
}
}
Ok(())
}
}
impl<'a> SubsetTable<'a> for IndexSubtableList<'a> {
type ArgsForSubset = (&'a Cbdt<'a>, &'a mut Vec<u8>);
type Output = (GlyphId, GlyphId, usize, usize);
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<Self::Output, SerializeErrorFlags> {
let records = self.index_subtable_records();
let src_num_records = records.len();
let mut obj_idxes = Vec::with_capacity(src_num_records);
let mut table_list_size = 0;
let init_len = s.length();
for idx in 0..src_num_records {
let record = records[src_num_records - 1 - idx];
let Ok(subtable) = record.index_subtable(self.offset_data()) else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
s.push()?;
match subset_index_subtable(&subtable, plan, s, &record, args.0, args.1) {
Ok((start_gid, end_gid, table_size)) => {
let Some(obj_idx) = s.pop_pack(true) else {
return Err(s.error());
};
obj_idxes.push((idx, obj_idx, start_gid, end_gid));
table_list_size += table_size;
}
Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY) => {
s.pop_discard();
continue;
}
Err(e) => {
return Err(e);
}
}
}
if obj_idxes.is_empty() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let mut min_start_gid = GlyphId::from(u16::MAX);
let mut max_end_gid = GlyphId::NOTDEF;
for (record_idx, objidx, start_gid, end_gid) in obj_idxes.iter().rev() {
let record = records[*record_idx];
record.subset(plan, s, (*objidx, *start_gid, *end_gid))?;
min_start_gid = min_start_gid.min(*start_gid);
max_end_gid = max_end_gid.max(*end_gid);
}
table_list_size += s.length() - init_len;
Ok((min_start_gid, max_end_gid, table_list_size, obj_idxes.len()))
}
}
impl SubsetTable<'_> for IndexSubtableRecord {
type ArgsForSubset = (usize, GlyphId, GlyphId);
type Output = ();
fn subset(
&self,
_plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<(), SerializeErrorFlags> {
s.embed(args.1.to_u32() as u16)?;
s.embed(args.2.to_u32() as u16)?;
let offset_pos = s.embed(0_u32)?;
let _ = s.add_link(
offset_pos..offset_pos + 4,
args.0,
OffsetWhence::Head,
0,
false,
);
Ok(())
}
}
fn subset_index_subtable(
table: &IndexSubtable,
plan: &Plan,
s: &mut Serializer,
index_subtable_record: &IndexSubtableRecord,
src_cbdt: &Cbdt,
cbdt_out: &mut Vec<u8>,
) -> Result<(GlyphId, GlyphId, usize), SerializeErrorFlags> {
match table {
IndexSubtable::Format1(item) => {
item.subset(plan, s, (index_subtable_record, src_cbdt, cbdt_out))
}
IndexSubtable::Format3(item) => {
item.subset(plan, s, (index_subtable_record, src_cbdt, cbdt_out))
}
_ => Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY),
}
}
impl<'a> SubsetTable<'a> for IndexSubtable1<'a> {
type ArgsForSubset = (&'a IndexSubtableRecord, &'a Cbdt<'a>, &'a mut Vec<u8>);
type Output = (GlyphId, GlyphId, usize);
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<(GlyphId, GlyphId, usize), SerializeErrorFlags> {
let src_record = args.0;
let src_gid_min = src_record.first_glyph_index();
let src_gid_max = src_record.last_glyph_index();
let mut retained_glyphs = IntSet::empty();
retained_glyphs.insert_range(GlyphId::from(src_gid_min)..=GlyphId::from(src_gid_max));
retained_glyphs.intersect(&plan.glyphset);
if retained_glyphs.is_empty() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let src_offsets = self.sbit_offsets();
let src_gid_min = src_gid_min.to_u32() as usize;
let mut end_glyph = None;
for gid in retained_glyphs.iter().rev() {
let idx = gid.to_u32() as usize - src_gid_min;
let offset_start = src_offsets[idx].get();
let offset_end = src_offsets[idx + 1].get();
if offset_end > offset_start {
end_glyph = Some(gid);
break;
}
}
if end_glyph.is_none() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let init_len = s.length();
s.embed(self.index_format())?;
s.embed(self.image_format())?;
let cbdt_out = args.2;
let image_offset = cbdt_out.len() as u32;
s.embed(image_offset)?;
let mut start_glyph = None;
let src_cbdt = args.1;
let mut cur_offset = 0_u32;
let mut last = 0;
for (new_gid, old_gid) in plan
.new_to_old_gid_list
.iter()
.filter(|&(_, g)| retained_glyphs.contains(*g))
{
let idx = old_gid.to_u32() as usize - src_gid_min;
let offset_start = src_offsets[idx].get();
let offset_end = src_offsets[idx + 1].get();
if start_glyph.is_some() {
while last < new_gid.to_u32() {
s.embed(cur_offset)?;
last += 1;
}
}
if offset_end <= offset_start {
if start_glyph.is_none() {
continue;
} else {
s.embed(cur_offset)?;
}
} else {
if start_glyph.is_none() {
start_glyph = Some(*new_gid);
last = new_gid.to_u32();
}
let src_glyph_offset = (self.image_data_offset() + offset_start) as usize;
let len = offset_end - offset_start;
let Some(glyph_data) = src_cbdt
.offset_data()
.as_bytes()
.get(src_glyph_offset..src_glyph_offset + len as usize)
else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
cbdt_out.extend_from_slice(glyph_data);
s.embed(cur_offset)?;
cur_offset += len;
}
last += 1;
if *old_gid == end_glyph.unwrap() {
end_glyph = Some(*new_gid);
s.embed(cur_offset)?;
break;
}
}
let len = s.length() - init_len;
Ok((start_glyph.unwrap(), end_glyph.unwrap(), len))
}
}
impl<'a> SubsetTable<'a> for IndexSubtable3<'a> {
type ArgsForSubset = (&'a IndexSubtableRecord, &'a Cbdt<'a>, &'a mut Vec<u8>);
type Output = (GlyphId, GlyphId, usize);
fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
args: Self::ArgsForSubset,
) -> Result<(GlyphId, GlyphId, usize), SerializeErrorFlags> {
let src_record = args.0;
let src_gid_min = src_record.first_glyph_index();
let src_gid_max = src_record.last_glyph_index();
let mut retained_glyphs = IntSet::empty();
retained_glyphs.insert_range(GlyphId::from(src_gid_min)..=GlyphId::from(src_gid_max));
retained_glyphs.intersect(&plan.glyphset);
if retained_glyphs.is_empty() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let src_offsets = self.sbit_offsets();
let src_gid_min = src_gid_min.to_u32() as usize;
let mut end_glyph = None;
for gid in retained_glyphs.iter().rev() {
let idx = gid.to_u32() as usize - src_gid_min;
let offset_start = src_offsets[idx].get();
let offset_end = src_offsets[idx + 1].get();
if offset_end > offset_start {
end_glyph = Some(gid);
break;
}
}
if end_glyph.is_none() {
return Err(SerializeErrorFlags::SERIALIZE_ERROR_EMPTY);
}
let init_len = s.length();
s.embed(self.index_format())?;
s.embed(self.image_format())?;
let cbdt_out = args.2;
let image_offset = cbdt_out.len() as u32;
s.embed(image_offset)?;
let mut start_glyph = None;
let src_cbdt = args.1;
let mut cur_offset = 0_u16;
let mut num_offsets: u32 = 0;
let mut last = 0;
for (new_gid, old_gid) in plan
.new_to_old_gid_list
.iter()
.filter(|&(_, g)| retained_glyphs.contains(*g))
{
let idx = old_gid.to_u32() as usize - src_gid_min;
let offset_start = src_offsets[idx].get();
let offset_end = src_offsets[idx + 1].get();
if start_glyph.is_some() {
while last < new_gid.to_u32() {
s.embed(cur_offset)?;
num_offsets += 1;
last += 1;
}
}
if offset_end <= offset_start {
if start_glyph.is_none() {
continue;
} else {
s.embed(cur_offset)?;
num_offsets += 1;
}
} else {
if start_glyph.is_none() {
start_glyph = Some(*new_gid);
last = new_gid.to_u32();
}
let src_glyph_offset = self.image_data_offset() as usize + offset_start as usize;
let len = offset_end - offset_start;
let Some(glyph_data) = src_cbdt
.offset_data()
.as_bytes()
.get(src_glyph_offset..src_glyph_offset + len as usize)
else {
return Err(s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR));
};
cbdt_out.extend_from_slice(glyph_data);
s.embed(cur_offset)?;
cur_offset += len;
num_offsets += 1;
}
last += 1;
if *old_gid == end_glyph.unwrap() {
end_glyph = Some(*new_gid);
s.embed(cur_offset)?;
num_offsets += 1;
break;
}
}
if num_offsets % 2 == 1 {
s.embed(0_u16)?;
}
let len = s.length() - init_len;
Ok((start_glyph.unwrap(), end_glyph.unwrap(), len))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_subset_cbdt_noop() {
let font = FontRef::new(include_bytes!(
"../test-data/fonts/NotoColorEmoji.subset.ttf"
))
.unwrap();
let cblc = font.cblc().unwrap();
let mut builder = FontBuilder::new();
let mut plan = Plan::default();
plan.glyphset
.insert_range(GlyphId::NOTDEF..=GlyphId::from(5_u32));
plan.glyph_map.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
plan.glyph_map
.insert(GlyphId::from(1_u32), GlyphId::from(1_u32));
plan.glyph_map
.insert(GlyphId::from(2_u32), GlyphId::from(2_u32));
plan.glyph_map
.insert(GlyphId::from(3_u32), GlyphId::from(3_u32));
plan.glyph_map
.insert(GlyphId::from(4_u32), GlyphId::from(4_u32));
plan.glyph_map
.insert(GlyphId::from(5_u32), GlyphId::from(5_u32));
plan.new_to_old_gid_list
.push((GlyphId::NOTDEF, GlyphId::NOTDEF));
plan.new_to_old_gid_list
.push((GlyphId::from(1_u32), GlyphId::from(1_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(2_u32), GlyphId::from(2_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(3_u32), GlyphId::from(3_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(4_u32), GlyphId::from(4_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(5_u32), GlyphId::from(5_u32)));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
let ret = cblc.subset(&plan, &font, &mut s, &mut builder);
assert!(ret.is_ok());
assert!(!s.in_error());
s.end_serialize();
let expected_bytes: [u8; 116] = [
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
0x00, 0x3C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE5, 0x88, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE5, 0x88, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x6D, 0x6D, 0x20, 0x01,
0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00,
0x00, 0x28, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x0E, 0xA5, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xA6, 0x00, 0x00, 0x07, 0x50, 0x00, 0x00, 0x14, 0xB7, 0x00, 0x01,
0x00, 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x52,
0x00, 0x00, 0x0E, 0xA1,
];
let subsetted_data = s.copy_bytes();
assert_eq!(subsetted_data, expected_bytes);
}
#[test]
fn test_subset_cbdt_keep_one() {
let font = FontRef::new(include_bytes!(
"../test-data/fonts/NotoColorEmoji.subset.ttf"
))
.unwrap();
let cblc = font.cblc().unwrap();
let mut builder = FontBuilder::new();
let mut plan = Plan::default();
plan.glyphset.insert(GlyphId::NOTDEF);
plan.glyphset.insert(GlyphId::from(2_u32));
plan.glyph_map.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
plan.glyph_map
.insert(GlyphId::from(2_u32), GlyphId::from(1_u32));
plan.new_to_old_gid_list
.push((GlyphId::NOTDEF, GlyphId::NOTDEF));
plan.new_to_old_gid_list
.push((GlyphId::from(1_u32), GlyphId::from(2_u32)));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
let ret = cblc.subset(&plan, &font, &mut s, &mut builder);
assert!(ret.is_ok());
assert!(!s.in_error());
s.end_serialize();
let subsetted_data = s.copy_bytes();
let expected_bytes: [u8; 80] = [
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x65, 0xe5, 0x88, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xe5, 0x88, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x6d, 0x6d, 0x20, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xaa,
];
assert_eq!(subsetted_data, expected_bytes);
}
#[test]
fn test_subset_cbdt_keep_one_last_subtable() {
let font = FontRef::new(include_bytes!(
"../test-data/fonts/NotoColorEmoji.subset.ttf"
))
.unwrap();
let cblc = font.cblc().unwrap();
let mut builder = FontBuilder::new();
let mut plan = Plan::default();
plan.glyphset.insert(GlyphId::NOTDEF);
plan.glyphset.insert(GlyphId::from(4_u32));
plan.glyph_map.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
plan.glyph_map
.insert(GlyphId::from(4_u32), GlyphId::from(1_u32));
plan.new_to_old_gid_list
.push((GlyphId::NOTDEF, GlyphId::NOTDEF));
plan.new_to_old_gid_list
.push((GlyphId::from(1_u32), GlyphId::from(4_u32)));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
let ret = cblc.subset(&plan, &font, &mut s, &mut builder);
assert!(ret.is_ok());
assert!(!s.in_error());
s.end_serialize();
let subsetted_data = s.copy_bytes();
let expected_bytes: [u8; 80] = [
00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x65, 0xe5, 0x88, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xe5, 0x88, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x6d, 0x6d, 0x20, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x52,
];
assert_eq!(subsetted_data, expected_bytes);
}
#[test]
fn test_subset_cbdt_keep_multiple_subtables() {
let font = FontRef::new(include_bytes!(
"../test-data/fonts/NotoColorEmoji.subset.multiple_size_tables.ttf"
))
.unwrap();
let cblc = font.cblc().unwrap();
let mut builder = FontBuilder::new();
let mut plan = Plan::default();
plan.glyphset.insert(GlyphId::NOTDEF);
plan.glyphset.insert(GlyphId::from(1_u32));
plan.glyphset.insert(GlyphId::from(3_u32));
plan.glyphset.insert(GlyphId::from(4_u32));
plan.glyph_map.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
plan.glyph_map
.insert(GlyphId::from(1_u32), GlyphId::from(1_u32));
plan.glyph_map
.insert(GlyphId::from(3_u32), GlyphId::from(2_u32));
plan.glyph_map
.insert(GlyphId::from(4_u32), GlyphId::from(3_u32));
plan.new_to_old_gid_list
.push((GlyphId::NOTDEF, GlyphId::NOTDEF));
plan.new_to_old_gid_list
.push((GlyphId::from(1_u32), GlyphId::from(1_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(2_u32), GlyphId::from(3_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(3_u32), GlyphId::from(4_u32)));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
let ret = cblc.subset(&plan, &font, &mut s, &mut builder);
assert!(ret.is_ok());
assert!(!s.in_error());
s.end_serialize();
let subsetted_data = s.copy_bytes();
let expected_bytes: [u8; 208] = [
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00,
0x00, 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE5, 0x88, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE5, 0x88, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0xD2, 0xD2, 0x20, 0x01,
0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x65, 0xE5, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x65, 0xE5, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x03, 0x6D, 0x6D, 0x20, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10,
0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00,
0x21, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xA6, 0x00, 0x00, 0x11, 0x0D,
0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x19, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x52, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x03,
0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x08, 0x56, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xA6, 0x00, 0x00, 0x11, 0x0D, 0x00, 0x01, 0x00, 0x11,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x52,
];
assert_eq!(subsetted_data, expected_bytes);
}
#[test]
fn test_subset_cbdt_index_format3() {
let font = FontRef::new(include_bytes!(
"../test-data/fonts/NotoColorEmoji.subset.index_format3.ttf"
))
.unwrap();
let cblc = font.cblc().unwrap();
let mut builder = FontBuilder::new();
let mut plan = Plan::default();
plan.glyphset.insert(GlyphId::NOTDEF);
plan.glyphset.insert(GlyphId::from(1_u32));
plan.glyphset.insert(GlyphId::from(3_u32));
plan.glyphset.insert(GlyphId::from(4_u32));
plan.glyph_map.insert(GlyphId::NOTDEF, GlyphId::NOTDEF);
plan.glyph_map
.insert(GlyphId::from(1_u32), GlyphId::from(1_u32));
plan.glyph_map
.insert(GlyphId::from(3_u32), GlyphId::from(2_u32));
plan.glyph_map
.insert(GlyphId::from(4_u32), GlyphId::from(3_u32));
plan.new_to_old_gid_list
.push((GlyphId::NOTDEF, GlyphId::NOTDEF));
plan.new_to_old_gid_list
.push((GlyphId::from(1_u32), GlyphId::from(1_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(2_u32), GlyphId::from(3_u32)));
plan.new_to_old_gid_list
.push((GlyphId::from(3_u32), GlyphId::from(4_u32)));
let mut s = Serializer::new(1024);
assert_eq!(s.start_serialize(), Ok(()));
let ret = cblc.subset(&plan, &font, &mut s, &mut builder);
assert!(ret.is_ok());
assert!(!s.in_error());
s.end_serialize();
let subsetted_data = s.copy_bytes();
let expected_bytes: [u8; 100] = [
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
0x00, 0x2C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE5, 0x88, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE5, 0x88, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x6D, 0x6D, 0x20, 0x01,
0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00,
0x00, 0x20, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x08, 0x56, 0x00, 0x00, 0x03, 0xA6,
0x11, 0x0D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x08, 0x52,
];
assert_eq!(subsetted_data, expected_bytes);
}
}