use std::{collections::BTreeMap, convert::TryFrom};
use types::{FixedSize, GlyphId16, Offset16};
use crate::{
tables::{
layout::{builders::Builder, CoverageTable},
variations::ivs_builder::VariationStoreBuilder,
},
FontWrite,
};
#[derive(Clone, Debug)]
struct TableSplitter<T: SplitTable> {
finished: Vec<T>,
current_coverage: Vec<GlyphId16>,
current_items: Vec<T::Component>,
current_size: usize,
}
trait SplitTable {
type Component;
fn size_for_item(item: &Self::Component) -> usize;
fn initial_size_for_item(item: &Self::Component) -> usize;
fn instantiate(coverage: CoverageTable, items: Vec<Self::Component>) -> Self;
}
impl<T: SplitTable + FontWrite> TableSplitter<T> {
const MAX_TABLE_SIZE: usize = u16::MAX as usize;
fn new() -> Self {
Self {
finished: Vec::new(),
current_coverage: Vec::new(),
current_items: Vec::new(),
current_size: 0,
}
}
fn add(&mut self, gid: GlyphId16, item: T::Component) {
let item_size = T::size_for_item(&item);
if item_size + self.current_size > Self::MAX_TABLE_SIZE {
let current_len = self.current_coverage.len();
self.finish_current();
let type_ = self.finished.last().unwrap().table_type();
log::info!("adding split in {type_} at {current_len}");
}
if self.current_size == 0 {
self.current_size = T::initial_size_for_item(&item);
}
self.current_coverage.push(gid);
self.current_items.push(item);
self.current_size += item_size + GlyphId16::RAW_BYTE_LEN;
}
fn finish_current(&mut self) {
if !self.current_coverage.is_empty() {
let coverage = std::mem::take(&mut self.current_coverage).into();
self.finished.push(T::instantiate(
coverage,
std::mem::take(&mut self.current_items),
));
self.current_size = 0;
}
}
fn finish(mut self) -> Vec<T> {
self.finish_current();
self.finished
}
}
#[derive(Clone, Debug, Default)]
pub struct SingleSubBuilder {
items: BTreeMap<GlyphId16, GlyphId16>,
}
impl SingleSubBuilder {
pub fn insert(&mut self, target: GlyphId16, replacement: GlyphId16) {
self.items.insert(target, replacement);
}
pub fn can_add(&self, target: GlyphId16, replacement: GlyphId16) -> bool {
!matches!(self.items.get(&target), Some(x) if *x != replacement)
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[deprecated(since = "0.38.2", note = "use ::iter instead")]
pub fn iter_pairs(&self) -> impl Iterator<Item = (GlyphId16, GlyphId16)> + '_ {
self.iter()
}
pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, GlyphId16)> + '_ {
self.items.iter().map(|(target, alt)| (*target, *alt))
}
pub fn promote_to_multi_sub(self) -> MultipleSubBuilder {
MultipleSubBuilder {
items: self
.items
.into_iter()
.map(|(key, gid)| (key, vec![gid]))
.collect(),
}
}
pub fn promote_to_ligature_sub(self) -> LigatureSubBuilder {
let mut items = BTreeMap::new();
for (from, to) in self.items.into_iter() {
items
.entry(from)
.or_insert(Vec::new())
.push((Vec::new(), to));
}
LigatureSubBuilder { items }
}
}
impl Builder for SingleSubBuilder {
type Output = Vec<super::SingleSubst>;
fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
if self.items.is_empty() {
return Default::default();
}
let delta = self
.items
.iter()
.map(|(k, v)| v.to_u16() as i32 - k.to_u16() as i32)
.reduce(|acc, val| if acc == val { acc } else { i32::MAX })
.and_then(|delta| i16::try_from(delta).ok());
let coverage = self.items.keys().copied().collect();
if let Some(delta) = delta {
vec![super::SingleSubst::format_1(coverage, delta)]
} else {
let replacements = self.items.values().copied().collect();
vec![super::SingleSubst::format_2(coverage, replacements)]
}
}
}
#[derive(Clone, Debug, Default)]
pub struct MultipleSubBuilder {
items: BTreeMap<GlyphId16, Vec<GlyphId16>>,
}
impl Builder for MultipleSubBuilder {
type Output = Vec<super::MultipleSubstFormat1>;
fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
let coverage = self.items.keys().copied().collect();
let seq_tables = self.items.into_values().map(super::Sequence::new).collect();
vec![super::MultipleSubstFormat1::new(coverage, seq_tables)]
}
}
impl MultipleSubBuilder {
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn insert(&mut self, target: GlyphId16, replacement: Vec<GlyphId16>) {
self.items.insert(target, replacement);
}
pub fn can_add(&self, target: GlyphId16, replacement: &[GlyphId16]) -> bool {
match self.items.get(&target) {
None => true,
Some(thing) => thing == replacement,
}
}
pub fn iter(&self) -> impl Iterator<Item = (&GlyphId16, &Vec<GlyphId16>)> {
self.items.iter()
}
}
#[derive(Clone, Debug, Default)]
pub struct AlternateSubBuilder {
items: BTreeMap<GlyphId16, Vec<GlyphId16>>,
}
impl AlternateSubBuilder {
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn insert(&mut self, target: GlyphId16, replacement: Vec<GlyphId16>) {
self.items.insert(target, replacement);
}
pub fn iter(&self) -> impl Iterator<Item = (&GlyphId16, &Vec<GlyphId16>)> {
self.items.iter()
}
pub fn iter_pairs(&self) -> impl Iterator<Item = (GlyphId16, GlyphId16)> + '_ {
self.items
.iter()
.flat_map(|(target, alt)| alt.iter().map(|alt| (*target, *alt)))
}
}
impl Builder for AlternateSubBuilder {
type Output = Vec<super::AlternateSubstFormat1>;
fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
let coverage = self.items.keys().copied().collect();
let seq_tables = self
.items
.into_values()
.map(super::AlternateSet::new)
.collect();
vec![super::AlternateSubstFormat1::new(coverage, seq_tables)]
}
}
#[derive(Clone, Debug, Default)]
pub struct LigatureSubBuilder {
items: BTreeMap<GlyphId16, Vec<(Vec<GlyphId16>, GlyphId16)>>,
}
impl LigatureSubBuilder {
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn insert(&mut self, target: Vec<GlyphId16>, replacement: GlyphId16) {
let (first, rest) = target.split_first().unwrap();
let entry = self.items.entry(*first).or_default();
if !entry
.iter()
.any(|existing| existing.0 == rest && existing.1 == replacement)
{
entry.push((rest.to_owned(), replacement))
}
}
pub fn can_add(&self, target: &[GlyphId16], replacement: GlyphId16) -> bool {
let Some((first, rest)) = target.split_first() else {
return false;
};
match self.items.get(first) {
Some(ligs) => !ligs
.iter()
.any(|(seq, target)| seq == rest && *target != replacement),
None => true,
}
}
pub fn iter(&self) -> impl Iterator<Item = (&GlyphId16, &Vec<(Vec<GlyphId16>, GlyphId16)>)> {
self.items.iter()
}
}
impl Builder for LigatureSubBuilder {
type Output = Vec<super::LigatureSubstFormat1>;
fn build(self, _: &mut VariationStoreBuilder) -> Self::Output {
let mut splitter = TableSplitter::<super::LigatureSubstFormat1>::new();
for (gid, mut ligs) in self.items.into_iter() {
ligs.sort_by_key(|(lig, _)| std::cmp::Reverse(lig.len()));
let lig_set = super::LigatureSet::new(
ligs.into_iter()
.map(|(components, replacement)| super::Ligature::new(replacement, components))
.collect(),
);
splitter.add(gid, lig_set);
}
splitter.finish()
}
}
impl SplitTable for super::LigatureSubstFormat1 {
type Component = super::LigatureSet;
fn size_for_item(item: &Self::Component) -> usize {
item.compute_size()
}
fn initial_size_for_item(_item: &Self::Component) -> usize {
u16::RAW_BYTE_LEN * 4
}
fn instantiate(coverage: CoverageTable, items: Vec<Self::Component>) -> Self {
Self::new(coverage, items)
}
}
impl super::LigatureSet {
fn compute_size(&self) -> usize {
u16::RAW_BYTE_LEN
+ Offset16::RAW_BYTE_LEN * self.ligatures.len()
+ self
.ligatures
.iter()
.map(|lig| lig.compute_size())
.sum::<usize>()
}
}
impl super::Ligature {
fn compute_size(&self) -> usize {
u16::RAW_BYTE_LEN
+ u16::RAW_BYTE_LEN
+ u16::RAW_BYTE_LEN * self.component_glyph_ids.len()
}
}
#[cfg(test)]
mod tests {
use crate::tables::gsub::LigatureSubstFormat1;
use super::*;
fn make_lig_table(n_bytes: u16, first: u16) -> super::super::Ligature {
assert!(n_bytes >= 6, "minimum table size");
assert!(n_bytes % 2 == 0, "can only generate even sizes: {n_bytes}");
let n_glyphs = (n_bytes - 6) / 2;
let components = (first..=first + n_glyphs).map(GlyphId16::new).collect();
super::super::Ligature::new(GlyphId16::new(first), components)
}
fn make_2048_bytes_of_ligature() -> super::super::LigatureSet {
let lig1 = make_lig_table(1022, 1);
let lig2 = make_lig_table(1022, 3);
super::super::LigatureSet::new(vec![lig1, lig2])
}
#[test]
fn who_tests_the_testers1() {
for size in [6, 12, 144, 2046, u16::MAX - 1] {
let table = make_lig_table(size, 1);
let bytes = crate::dump_table(&table).unwrap();
assert_eq!(bytes.len(), size as usize);
}
}
#[test]
fn splitting_ligature_subs() {
let mut splitter = TableSplitter::<LigatureSubstFormat1>::new();
let ligset = make_2048_bytes_of_ligature();
for gid in 0u16..31 {
splitter.add(GlyphId16::new(gid), ligset.clone());
}
assert_eq!(splitter.clone().finish().len(), 1);
splitter.add(GlyphId16::new(32), ligset);
assert_eq!(splitter.finish().len(), 2)
}
}