diff --git a/fontc/src/change_detector.rs b/fontc/src/change_detector.rs index 2b25f735a..33d7bedc4 100644 --- a/fontc/src/change_detector.rs +++ b/fontc/src/change_detector.rs @@ -129,15 +129,6 @@ impl ChangeDetector { .is_file() } - pub fn kerning_ir_change(&self) -> bool { - self.final_static_metadata_ir_change() - || self.current_inputs.features != self.prev_inputs.features - || !self - .ir_paths - .target_file(&FeWorkIdentifier::Kerning) - .is_file() - } - pub fn avar_be_change(&self) -> bool { self.final_static_metadata_ir_change() || !self.be_paths.target_file(&BeWorkIdentifier::Avar).is_file() diff --git a/fontc/src/lib.rs b/fontc/src/lib.rs index 9a6212728..e78738df5 100644 --- a/fontc/src/lib.rs +++ b/fontc/src/lib.rs @@ -256,34 +256,6 @@ fn add_feature_be_job( Ok(()) } -fn add_kerning_ir_job( - change_detector: &mut ChangeDetector, - workload: &mut Workload, -) -> Result<(), Error> { - if change_detector.kerning_ir_change() { - let mut dependencies = HashSet::new(); - dependencies.insert(FeWorkIdentifier::FinalizeStaticMetadata.into()); - - let id: AnyWorkId = FeWorkIdentifier::Kerning.into(); - let write_access = Access::one(id.clone()); - workload.insert( - id, - Job { - work: change_detector - .ir_source() - .create_kerning_ir_work(change_detector.current_inputs())? - .into(), - dependencies, - read_access: ReadAccess::Dependencies, - write_access, - }, - ); - } else { - workload.mark_success(FeWorkIdentifier::Kerning); - } - Ok(()) -} - fn add_glyph_ir_jobs( change_detector: &mut ChangeDetector, workload: &mut Workload, @@ -771,7 +743,6 @@ pub fn create_workload(change_detector: &mut ChangeDetector) -> Result = kerning - .groups - .iter() - .map(|(name, entries)| { - let mut entries: Vec<_> = entries.iter().map(|e| e.as_str()).collect(); - entries.sort(); - (name.as_str(), entries) - }) - .collect(); - groups.sort(); - - let mut kerns: Vec<_> = kerning - .kerns - .iter() - .map(|((side1, side2), values)| { - ( - side1, - side2, - values - .iter() - .map(|(loc, val)| { - assert_eq!(loc.axis_names().count(), 1, "Should be only weight"); - let (axis, pos) = loc.iter().next().unwrap(); - (format!("{axis} {}", pos.to_f32()), val.0) - }) - .collect::>(), - ) - }) - .collect(); - kerns.sort_by_key(|(side1, side2, _)| (*side1, *side2)); - - assert_eq!( - (groups, kerns), - ( - vec![ - ("public.kern1.brackets", vec!["bracketleft", "bracketright"],), - ("public.kern2.brackets", vec!["bracketleft", "bracketright"],) - ], - vec![ - ( - &KernParticipant::Glyph("bracketleft".into()), - &KernParticipant::Glyph("bracketright".into()), - vec![ - ("Weight 0".to_string(), -300.0), - ("Weight 1".to_string(), -150.0) - ], - ), - ( - &KernParticipant::Glyph("exclam".into()), - &KernParticipant::Glyph("exclam".into()), - vec![ - ("Weight 0".to_string(), -360.0), - ("Weight 1".to_string(), -100.0) - ], - ), - ( - &KernParticipant::Glyph("exclam".into()), - &KernParticipant::Glyph("hyphen".into()), - vec![("Weight 0".to_string(), 20.0),], - ), - ( - &KernParticipant::Glyph("exclam".into()), - &KernParticipant::Group("public.kern2.brackets".into()), - vec![("Weight 0".to_string(), -160.0),], - ), - ( - &KernParticipant::Glyph("hyphen".into()), - &KernParticipant::Glyph("hyphen".into()), - vec![ - ("Weight 0".to_string(), -150.0), - ("Weight 1".to_string(), -50.0) - ], - ), - ( - &KernParticipant::Group("public.kern1.brackets".into()), - &KernParticipant::Glyph("exclam".into()), - vec![("Weight 0".to_string(), -165.0),], - ), - ], - ), - ); - } - - #[test] - fn kerning_from_glyphs() { - assert_simple_kerning("glyphs3/WghtVar.glyphs"); - } - - #[test] - fn kerning_from_ufo() { - assert_simple_kerning("designspace_from_glyphs/WghtVar.designspace"); - } } diff --git a/fontdrasil/src/types.rs b/fontdrasil/src/types.rs index 8e9da6b82..0e47246fa 100644 --- a/fontdrasil/src/types.rs +++ b/fontdrasil/src/types.rs @@ -49,5 +49,3 @@ impl Display for GlyphName { f.write_str(self.as_str()) } } - -pub type GroupName = GlyphName; diff --git a/fontir/src/error.rs b/fontir/src/error.rs index 324d73fe6..bc0ce4ca4 100644 --- a/fontir/src/error.rs +++ b/fontir/src/error.rs @@ -130,8 +130,6 @@ pub enum WorkError { AxisMustMapMin(Tag), #[error("Axis '{0}' must map max if it maps anything")] AxisMustMapMax(Tag), - #[error("No kerning group or glyph for name {0:?}")] - InvalidKernSide(String), } /// An async work error, hence one that must be Send diff --git a/fontir/src/ir.rs b/fontir/src/ir.rs index 002e21787..0ad8584e3 100644 --- a/fontir/src/ir.rs +++ b/fontir/src/ir.rs @@ -3,23 +3,20 @@ use crate::{ coords::{CoordConverter, NormalizedCoord, NormalizedLocation, UserCoord, UserLocation}, error::{PathConversionError, VariationModelError, WorkError}, - serde::{ - GlobalMetricsSerdeRepr, GlyphSerdeRepr, KerningSerdeRepr, MiscSerdeRepr, - StaticMetadataSerdeRepr, - }, + serde::{GlobalMetricsSerdeRepr, GlyphSerdeRepr, MiscSerdeRepr, StaticMetadataSerdeRepr}, variations::VariationModel, }; use chrono::{DateTime, Utc}; use font_types::NameId; use font_types::Tag; -use fontdrasil::types::{GlyphName, GroupName}; +use fontdrasil::types::GlyphName; use indexmap::IndexSet; use kurbo::{Affine, BezPath, PathEl, Point}; use log::{trace, warn}; use ordered_float::OrderedFloat; use serde::{Deserialize, Serialize}; use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{HashMap, HashSet}, fmt::Debug, path::{Path, PathBuf}, }; @@ -98,37 +95,6 @@ pub struct MiscMetadata { pub created: Option>, } -/// IR representation of kerning. -/// -/// In UFO terms, roughly [groups.plist](https://unifiedfontobject.org/versions/ufo3/groups.plist/) -/// and [kerning.plist](https://unifiedfontobject.org/versions/ufo3/kerning.plist/) combined. -#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)] -#[serde(from = "KerningSerdeRepr", into = "KerningSerdeRepr")] -pub struct Kerning { - pub groups: HashMap>, - /// An adjustment to the space *between* two glyphs in logical order. - /// - /// Maps (side1, side2) => a mapping location:adjustment. - /// - /// Used for both LTR and RTL. The BE application differs but the concept - /// is the same. - pub kerns: HashMap< - (KernParticipant, KernParticipant), - BTreeMap>, - >, -} - -/// A participant in kerning, one of the entries in a kerning pair. -/// -/// Concretely, a glyph or a group of glyphs. -/// -/// -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum KernParticipant { - Glyph(GlyphName), - Group(GroupName), -} - impl StaticMetadata { pub fn new( units_per_em: u16, diff --git a/fontir/src/orchestration.rs b/fontir/src/orchestration.rs index dd203d6e3..c93d219d1 100644 --- a/fontir/src/orchestration.rs +++ b/fontir/src/orchestration.rs @@ -106,7 +106,6 @@ pub enum WorkId { /// BE glyphs so the glyph order may change. FinalizeStaticMetadata, Features, - Kerning, } pub type IrWork = dyn Work + Send; @@ -134,7 +133,6 @@ pub struct Context { global_metrics: ContextItem, glyph_ir: Arc>>>, feature_ir: ContextItem, - kerning: ContextItem, } pub fn set_cached(lock: &Arc>>>, value: T) { @@ -154,7 +152,6 @@ impl Context { global_metrics: self.global_metrics.clone(), glyph_ir: self.glyph_ir.clone(), feature_ir: self.feature_ir.clone(), - kerning: self.kerning.clone(), } } @@ -169,7 +166,6 @@ impl Context { global_metrics: Arc::from(RwLock::new(None)), glyph_ir: Arc::from(RwLock::new(HashMap::new())), feature_ir: Arc::from(RwLock::new(None)), - kerning: Arc::from(RwLock::new(None)), } } @@ -238,7 +234,6 @@ impl Context { context_accessors! { get_final_static_metadata, set_final_static_metadata, has_final_static_metadata, final_static_metadata, ir::StaticMetadata, WorkId::FinalizeStaticMetadata, restore, nop } context_accessors! { get_global_metrics, set_global_metrics, has_global_metrics, global_metrics, ir::GlobalMetrics, WorkId::GlobalMetrics, restore, nop } context_accessors! { get_features, set_features, has_feature_ir, feature_ir, ir::Features, WorkId::Features, restore, nop } - context_accessors! { get_kerning, set_kerning, has_kerning, kerning, ir::Kerning, WorkId::Kerning, restore, nop } } fn nop(v: &T) -> &T { diff --git a/fontir/src/paths.rs b/fontir/src/paths.rs index ef77e3bad..d65231a3e 100644 --- a/fontir/src/paths.rs +++ b/fontir/src/paths.rs @@ -49,7 +49,6 @@ impl Paths { WorkId::Glyph(name) => self.glyph_ir_file(name.as_str()), WorkId::GlyphIrDelete => self.build_dir.join("delete.yml"), WorkId::Features => self.build_dir.join("features.yml"), - WorkId::Kerning => self.build_dir.join("kerning.yml"), } } } diff --git a/fontir/src/serde.rs b/fontir/src/serde.rs index f1b8d7926..928f5f32a 100644 --- a/fontir/src/serde.rs +++ b/fontir/src/serde.rs @@ -13,8 +13,8 @@ use write_fonts::tables::os2::SelectionFlags; use crate::{ coords::{CoordConverter, DesignCoord, NormalizedLocation, UserCoord}, ir::{ - Axis, GlobalMetric, GlobalMetrics, Glyph, GlyphBuilder, GlyphInstance, KernParticipant, - Kerning, MiscMetadata, NameKey, NamedInstance, StaticMetadata, + Axis, GlobalMetric, GlobalMetrics, Glyph, GlyphBuilder, GlyphInstance, MiscMetadata, + NameKey, NamedInstance, StaticMetadata, }, stateset::{FileState, MemoryState, State, StateIdentifier, StateSet}, }; @@ -96,88 +96,6 @@ impl From for StaticMetadataSerdeRepr { } } -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct KerningSerdeRepr { - pub groups: Vec, - pub kerns: Vec, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct KerningGroupSerdeRepr { - name: String, - glyphs: Vec, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct KernSerdeRepr { - side1: KernParticipant, - side2: KernParticipant, - values: Vec<(NormalizedLocation, f32)>, -} - -impl From for KerningSerdeRepr { - fn from(from: Kerning) -> Self { - KerningSerdeRepr { - groups: from - .groups - .into_iter() - .map(|(name, glyphs)| { - let name = name.to_string(); - let mut glyphs: Vec<_> = glyphs.iter().map(|g| g.to_string()).collect(); - glyphs.sort(); - KerningGroupSerdeRepr { name, glyphs } - }) - .collect(), - kerns: from - .kerns - .into_iter() - .map(|((side1, side2), values)| { - let mut values: Vec<_> = values - .into_iter() - .map(|(pos, adjustment)| (pos, adjustment.0)) - .collect(); - values.sort_by_key(|(pos, _)| pos.clone()); - KernSerdeRepr { - side1, - side2, - values, - } - }) - .collect(), - } - } -} - -impl From for Kerning { - fn from(from: KerningSerdeRepr) -> Self { - Kerning { - groups: from - .groups - .into_iter() - .map(|g| { - ( - g.name.into(), - g.glyphs.into_iter().map(|n| n.into()).collect(), - ) - }) - .collect(), - kerns: from - .kerns - .into_iter() - .map(|k| { - ( - (k.side1, k.side2), - k.values - .into_iter() - .map(|(pos, value)| (pos, value.into())) - .collect(), - ) - }) - .collect(), - } - } -} - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct MiscSerdeRepr { pub selection_flags: u16, diff --git a/fontir/src/source.rs b/fontir/src/source.rs index c2ad8158c..41985fa44 100644 --- a/fontir/src/source.rs +++ b/fontir/src/source.rs @@ -69,11 +69,6 @@ pub trait Source { /// /// When run work should update [Context] with [crate::ir::Features]. fn create_feature_ir_work(&self, input: &Input) -> Result, Error>; - - /// Create a function that could be called to generate or identify kerning. - /// - /// When run work should update [Context] with [crate::ir::Kerning]. - fn create_kerning_ir_work(&self, input: &Input) -> Result, Error>; } /// The files (in future non-file sources?) that drive various parts of IR diff --git a/glyphs2fontir/src/source.rs b/glyphs2fontir/src/source.rs index bb6893bfa..cd2e281bf 100644 --- a/glyphs2fontir/src/source.rs +++ b/glyphs2fontir/src/source.rs @@ -1,12 +1,12 @@ use chrono::{TimeZone, Utc}; use font_types::{NameId, Tag}; use fontdrasil::orchestration::Work; -use fontdrasil::types::{GlyphName, GroupName}; +use fontdrasil::types::GlyphName; use fontir::coords::NormalizedCoord; use fontir::error::{Error, WorkError}; use fontir::ir::{ - self, GlobalMetric, GlobalMetrics, GlyphInstance, KernParticipant, Kerning, NameBuilder, - NameKey, NamedInstance, StaticMetadata, DEFAULT_VENDOR_ID, + self, GlobalMetric, GlobalMetrics, GlyphInstance, NameBuilder, NameKey, NamedInstance, + StaticMetadata, DEFAULT_VENDOR_ID, }; use fontir::orchestration::{Context, IrWork}; use fontir::source::{Input, Source}; @@ -223,16 +223,6 @@ impl Source for GlyphsIrSource { font_info: cache.font_info.clone(), })) } - - fn create_kerning_ir_work(&self, input: &Input) -> Result, Error> { - self.check_static_metadata(&input.static_metadata)?; - - let cache = self.cache.as_ref().unwrap(); - - Ok(Box::new(KerningWork { - font_info: cache.font_info.clone(), - })) - } } fn try_name_id(name: &str) -> Option { @@ -468,138 +458,6 @@ impl Work for FeatureWork { } } -/// What side of the kern is this, in logical order -enum KernSide { - L, - R, -} - -impl KernSide { - fn class_prefix(&self) -> &'static str { - match self { - KernSide::L => "@MMK_L_", - KernSide::R => "@MMK_R_", - } - } - - fn group_prefix(&self) -> &'static str { - match self { - KernSide::L => "public.kern1.", - KernSide::R => "public.kern2.", - } - } -} - -struct KerningWork { - font_info: Arc, -} - -/// See -fn kern_participant( - glyph_order: &IndexSet, - groups: &HashMap>, - side: KernSide, - raw_side: &str, -) -> Option { - if raw_side.starts_with(side.class_prefix()) { - let group_name = format!( - "{}{}", - side.group_prefix(), - raw_side.strip_prefix(side.class_prefix()).unwrap() - ); - let group = GroupName::from(&group_name); - if groups.contains_key(&group) { - Some(KernParticipant::Group(group)) - } else { - warn!("Invalid kern side: {raw_side}, no group {group_name}"); - None - } - } else { - let name = GlyphName::from(raw_side); - if glyph_order.contains(&name) { - Some(KernParticipant::Glyph(name)) - } else { - warn!("Invalid kern side: {raw_side}, no such glyph"); - None - } - } -} - -impl Work for KerningWork { - fn exec(&self, context: &Context) -> Result<(), WorkError> { - trace!("Generate IR for kerning"); - let static_metadata = context.get_final_static_metadata(); - let glyph_order = &static_metadata.glyph_order; - let font_info = self.font_info.as_ref(); - let font = &font_info.font; - - let master_positions: HashMap<_, _> = font - .masters - .iter() - .map(|m| (&m.id, font_info.locations.get(&m.axes_values).unwrap())) - .collect(); - - let mut kerning = Kerning::default(); - - // If glyph uses a group for either side it goes in that group - font.glyphs - .iter() - .flat_map(|(glyph_name, glyph)| { - glyph - .left_kern - .iter() - .map(|group| (KernSide::L, group)) - .chain(glyph.right_kern.iter().map(|group| (KernSide::R, group))) - .map(|(side, group_name)| { - ( - GroupName::from(format!("{}{}", side.group_prefix(), group_name)), - GlyphName::from(glyph_name.as_str()), - ) - }) - }) - .for_each(|(group_name, glyph_name)| { - kerning - .groups - .entry(group_name) - .or_default() - .insert(glyph_name); - }); - - font.kerning_ltr - .iter() - .filter_map(|(master_id, kerns)| match master_positions.get(master_id) { - Some(pos) => Some((pos, kerns)), - None => { - warn!("Kerning is present for non-existent master {master_id}"); - None - } - }) - .flat_map(|(master_pos, kerns)| { - kerns.iter().map(|((side1, side2), adjustment)| { - ((side1, side2), ((*master_pos).clone(), *adjustment)) - }) - }) - .filter_map(|((side1, side2), pos_adjust)| { - let side1 = kern_participant(glyph_order, &kerning.groups, KernSide::L, side1); - let side2 = kern_participant(glyph_order, &kerning.groups, KernSide::R, side2); - let (Some(side1), Some(side2)) = (side1, side2) else { - return None - }; - Some(((side1, side2), pos_adjust)) - }) - .for_each(|(participants, (pos, value))| { - kerning - .kerns - .entry(participants) - .or_default() - .insert(pos, (value as f32).into()); - }); - - context.set_kerning(kerning); - Ok(()) - } -} - struct GlyphIrWork { glyph_name: GlyphName, font_info: Arc, diff --git a/ufo2fontir/src/source.rs b/ufo2fontir/src/source.rs index 2adad62c6..fab13781f 100644 --- a/ufo2fontir/src/source.rs +++ b/ufo2fontir/src/source.rs @@ -7,16 +7,13 @@ use std::{ use chrono::{DateTime, TimeZone, Utc}; use font_types::{NameId, Tag}; -use fontdrasil::{ - orchestration::Work, - types::{GlyphName, GroupName}, -}; +use fontdrasil::{orchestration::Work, types::GlyphName}; use fontir::{ coords::{DesignLocation, NormalizedLocation, UserCoord}, error::{Error, WorkError}, ir::{ - Features, GlobalMetric, GlobalMetrics, KernParticipant, Kerning, NameBuilder, NameKey, - NamedInstance, StaticMetadata, DEFAULT_VENDOR_ID, + Features, GlobalMetric, GlobalMetrics, NameBuilder, NameKey, NamedInstance, StaticMetadata, + DEFAULT_VENDOR_ID, }, orchestration::{Context, IrWork}, source::{Input, Source}, @@ -349,16 +346,6 @@ impl Source for DesignSpaceIrSource { })) } - fn create_kerning_ir_work(&self, input: &Input) -> Result, Error> { - self.check_static_metadata(&input.static_metadata)?; - let cache = self.cache.as_ref().unwrap(); - - Ok(Box::new(KerningWork { - designspace_file: cache.designspace_file.clone(), - designspace: cache.designspace.clone(), - })) - } - fn create_glyph_ir_work( &self, glyph_names: &IndexSet, @@ -395,11 +382,6 @@ struct FeatureWork { fea_files: Arc>, } -struct KerningWork { - designspace_file: PathBuf, - designspace: Arc, -} - fn default_master(designspace: &DesignSpaceDocument) -> Option<(usize, &designspace::Source)> { let ds_axes = to_ir_axes(&designspace.axes).ok()?; let axes: HashMap<_, _> = ds_axes.iter().map(|a| (&a.name, a)).collect(); @@ -863,79 +845,6 @@ impl Work for FeatureWork { } } -impl Work for KerningWork { - fn exec(&self, context: &Context) -> Result<(), WorkError> { - debug!("Kerning for {:#?}", self.designspace_file); - - let designspace_dir = self.designspace_file.parent().unwrap(); - let static_metadata = context.get_final_static_metadata(); - let master_locations = master_locations(&static_metadata.axes, &self.designspace.sources); - - let mut kerning = Kerning::default(); - for source in self.designspace.sources.iter() { - let pos = master_locations.get(&source.name).unwrap(); - let ufo_dir = designspace_dir.join(&source.filename); - let data_request = norad::DataRequest::none().groups(true).kerning(true); - let font = norad::Font::load_requested_data(&ufo_dir, data_request) - .map_err(|e| WorkError::ParseError(ufo_dir, format!("{e}")))?; - - kerning.groups = font - .groups - .into_iter() - .map(|(group_name, entries)| { - ( - GroupName::from(group_name.as_str()), - entries - .into_iter() - .filter_map(|glyph_name| { - let glyph_name = GlyphName::from(glyph_name.as_str()); - if static_metadata.glyph_order.contains(&glyph_name) { - Some(glyph_name) - } else { - warn!("{} kerning group {} references non-existent glyph {}; ignoring", source.filename, group_name, glyph_name); - None - } - }) - .collect(), - ) - }) - .collect(); - - let resolve_participant = |name: &norad::Name| { - let group_name = GroupName::from(name.as_str()); - if kerning.groups.contains_key(&group_name) { - return Some(KernParticipant::Group(group_name)); - } - let glyph_name = GlyphName::from(name.as_str()); - if static_metadata.glyph_order.contains(&glyph_name) { - return Some(KernParticipant::Glyph(glyph_name)); - } - None - }; - - for (side1, side2, adjustment) in font.kerning.into_iter().flat_map(|(side1, kerns)| { - kerns - .into_iter() - .map(move |(side2, adjustment)| (side1.clone(), side2, adjustment)) - }) { - let (Some(side1), Some(side2)) = (resolve_participant(&side1), resolve_participant(&side2)) else { - warn!("{} kerning unable to resolve at least one of {}, {}; ignoring", source.name, side1.as_str(), side2.as_str()); - continue; - }; - - kerning - .kerns - .entry((side1, side2)) - .or_default() - .insert(pos.clone(), (adjustment as f32).into()); - } - } - - context.set_kerning(kerning); - Ok(()) - } -} - struct GlyphIrWork { glyph_name: GlyphName, glif_files: HashMap>,