use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::fmt::Display;
use write_fonts::types::GlyphId16;
use write_fonts::tables::{
self,
gdef::{
AttachList, AttachPoint, CaretValue as RawCaretValue, GlyphClassDef, LigCaretList,
LigGlyph, MarkGlyphSets,
},
layout::{
builders::{CaretValueBuilder, CoverageTableBuilder},
ClassDef,
},
variations::ivs_builder::RemapVariationIndices,
};
use super::{VariationIndexRemapping, VariationStoreBuilder};
use crate::common::{GlyphClass, GlyphSet};
#[derive(Clone, Debug, Default)]
pub struct GdefBuilder {
pub glyph_classes: HashMap<GlyphId16, GlyphClassDef>,
pub glyph_classes_were_inferred: bool,
pub attach: BTreeMap<GlyphId16, BTreeSet<u16>>,
pub ligature_pos: BTreeMap<GlyphId16, Vec<CaretValueBuilder>>,
pub mark_attach_class: BTreeMap<GlyphId16, u16>,
pub mark_glyph_sets: Vec<GlyphSet>,
pub var_store: Option<VariationStoreBuilder>,
}
impl GdefBuilder {
pub fn build(&self) -> (tables::gdef::Gdef, Option<VariationIndexRemapping>) {
let mut var_store = self
.var_store
.clone()
.unwrap_or_else(|| VariationStoreBuilder::new(0));
let mut table = tables::gdef::Gdef::new(
self.build_class_def(),
self.build_attach_list(),
self.build_lig_caret_list(&mut var_store),
self.build_mark_attach_class_def(),
);
table.mark_glyph_sets_def = self.build_mark_glyph_sets().into();
if !var_store.is_empty() {
let (var_store, key_map) = var_store.build();
table.item_var_store.set(var_store);
table.remap_variation_indices(&key_map);
(table, Some(key_map))
} else {
(table, None)
}
}
fn build_class_def(&self) -> Option<ClassDef> {
(!self.glyph_classes.is_empty()).then(|| {
self.glyph_classes
.iter()
.map(|(gid, cls)| (*gid, *cls as u16))
.collect()
})
}
fn build_attach_list(&self) -> Option<AttachList> {
let mut coverage = CoverageTableBuilder::default();
let mut attach_points = Vec::new();
for (glyph, points) in &self.attach {
coverage.add(*glyph);
attach_points.push(AttachPoint::new(points.iter().copied().collect()));
}
(!attach_points.is_empty()).then(|| AttachList::new(coverage.build(), attach_points))
}
fn build_lig_caret_list(&self, var_store: &mut VariationStoreBuilder) -> Option<LigCaretList> {
let mut coverage = CoverageTableBuilder::default();
let mut lig_glyphs = Vec::new();
for (glyph, carets) in &self.ligature_pos {
coverage.add(*glyph);
let mut carets = carets
.iter()
.map(|caret| caret.clone().build(var_store))
.collect::<Vec<_>>();
carets.sort_by_key(|c| match c {
RawCaretValue::Format1(table) => table.coordinate as i32,
RawCaretValue::Format2(table) => table.caret_value_point_index as i32,
RawCaretValue::Format3(table) => table.coordinate as i32,
});
lig_glyphs.push(LigGlyph::new(carets));
}
(!lig_glyphs.is_empty()).then(|| LigCaretList::new(coverage.build(), lig_glyphs))
}
fn build_mark_glyph_sets(&self) -> Option<MarkGlyphSets> {
(!self.mark_glyph_sets.is_empty()).then(|| {
MarkGlyphSets::new(
self.mark_glyph_sets
.iter()
.map(|cls| cls.iter().collect::<CoverageTableBuilder>().build())
.collect(),
)
})
}
fn build_mark_attach_class_def(&self) -> Option<ClassDef> {
(!self.mark_attach_class.is_empty()).then(|| {
self.mark_attach_class
.iter()
.map(|(a, b)| (*a, *b))
.collect()
})
}
pub(crate) fn add_glyph_class(
&mut self,
glyphs: GlyphClass,
class: GlyphClassDef,
) -> Result<(), (GlyphId16, GlyphClassDef)> {
for glyph in glyphs.iter() {
if let Some(prev_class) = self.glyph_classes.insert(glyph, class) {
if prev_class != class {
return Err((glyph, prev_class));
}
}
}
Ok(())
}
pub(crate) fn is_empty(&self) -> bool {
self.glyph_classes.is_empty()
&& self.attach.is_empty()
&& self.ligature_pos.is_empty()
&& self.mark_attach_class.is_empty()
&& self.mark_glyph_sets.is_empty()
&& self.var_store.is_none()
}
}
pub(crate) trait GlyphClassDefExt {
fn display(&self) -> impl Display;
}
impl GlyphClassDefExt for GlyphClassDef {
fn display(&self) -> impl Display {
match self {
GlyphClassDef::Base => "Base",
GlyphClassDef::Ligature => "Ligature",
GlyphClassDef::Mark => "Mark",
GlyphClassDef::Component => "Component",
_ => "Invalid",
}
}
}