blob: d0dc451f7d55d21666b58e1a708d9a18e288e1f7 [file] [edit]
//! impl subset() for name table
use crate::{
serialize::{OffsetWhence, Serializer},
Plan, Subset,
SubsetError::{self, SubsetTableError},
SubsetFlags,
};
use write_fonts::{
read::{
tables::name::{Name, NameRecord},
FontRef, TopLevelTable,
},
types::FixedSize,
FontBuilder,
};
// reference: subset() for name table in harfbuzz
// https://github.com/harfbuzz/harfbuzz/blob/a070f9ebbe88dc71b248af9731dd49ec93f4e6e6/src/OT/name/name.hh#L387
impl Subset for Name<'_> {
fn subset(
&self,
plan: &Plan,
_font: &FontRef,
s: &mut Serializer,
_builder: &mut FontBuilder,
) -> Result<(), SubsetError> {
let name_records = self.name_record();
//TODO: support name_table_override
//TODO: support name table version 1
let mut retained_name_record_idxes = name_records
.iter()
.enumerate()
.filter_map(|(idx, record)| {
if !plan.name_ids.contains(record.name_id())
|| !plan.name_languages.contains(record.language_id())
|| (!plan
.subset_flags
.contains(SubsetFlags::SUBSET_FLAGS_NAME_LEGACY)
&& !record.is_unicode())
{
return None;
}
Some(idx)
})
.collect::<Vec<_>>();
retained_name_record_idxes.sort_unstable_by_key(|nr| {
let nr = name_records[*nr];
(
nr.platform_id(),
nr.encoding_id(),
nr.language_id(),
nr.name_id().to_u16(),
nr.length(),
)
});
// version
// TODO: support version 1
s.embed(0_u16)
.map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
//count
let count = retained_name_record_idxes.len() as u16;
s.embed(count)
.map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
//storage_offset
let storage_offset = count * NameRecord::RAW_BYTE_LEN as u16 + 6;
s.embed(storage_offset)
.map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
serialize_name_records(self, s, &retained_name_record_idxes)
}
}
fn serialize_name_records(
name: &Name,
s: &mut Serializer,
retained_name_record_idxes: &[usize],
) -> Result<(), SubsetError> {
let data = name.offset_data().as_bytes();
let name_records = name.name_record();
let name_records_bytes = data.get(name.shape().name_record_byte_range()).unwrap();
let storage_start = name.storage_offset() as usize;
for idx in retained_name_record_idxes.iter() {
let len = s.length();
let record_pos = idx * NAME_RECORD_SIZE;
let record_bytes = name_records_bytes
.get(record_pos..record_pos + NAME_RECORD_SIZE)
.ok_or(SubsetError::SubsetTableError(Name::TAG))?;
s.embed_bytes(record_bytes)
.map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
let record = name_records[*idx];
let offset = record.string_offset().to_u32() as usize;
// 10 is the position of offset field within a NameRecord
let offset_pos = len + 10;
s.push()
.map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
//copy string data
let str_start = storage_start + offset;
let str_len = record.length();
let str_bytes = data
.get(str_start..str_start + str_len as usize)
.ok_or(SubsetTableError(Name::TAG))?;
s.embed_bytes(str_bytes)
.map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
let obj_idx = s
.pop_pack(true)
.ok_or(SubsetError::SubsetTableError(Name::TAG))?;
s.add_link(
offset_pos..offset_pos + 2,
obj_idx,
OffsetWhence::Tail,
0,
false,
)
.map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
}
Ok(())
}
//NameRecord size in bytes
const NAME_RECORD_SIZE: usize = NameRecord::RAW_BYTE_LEN;