Skip to content

Commit

Permalink
Shuffle to sit on feature writer APIs instead of generating a string.…
Browse files Browse the repository at this point in the history
… Generate a string anyway for irony.
  • Loading branch information
rsheeter committed Oct 23, 2023
1 parent 08ed079 commit 75d75e5
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 17 deletions.
146 changes: 129 additions & 17 deletions fontbe/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ use fontir::{
use log::{debug, error, trace, warn};
use ordered_float::OrderedFloat;

use write_fonts::{tables::gpos::ValueRecord, tables::layout::LookupFlag, OtRound};
use fontdrasil::{
orchestration::{Access, Work},
types::GlyphName,
};
use write_fonts::{tables::gpos::{ValueRecord, AnchorTable, AnchorFormat1}, tables::layout::LookupFlag, OtRound};

use crate::{
error::Error,
Expand Down Expand Up @@ -217,6 +217,13 @@ impl<'a> FeatureWriter<'a> {
}
}

fn glyph_id(&self, glyph_name: &GlyphName) -> Option<GlyphId> {
self.glyph_map
.glyph_id(glyph_name)
.map(|val| Some(GlyphId::new(val as u16)))
.unwrap_or_default()
}

//TODO: at least for kerning, we should be able to generate the lookups
//as a separate worktask, and then just add them here.
fn add_kerning_features(&self, builder: &mut FeatureBuilder) {
Expand All @@ -226,10 +233,7 @@ impl<'a> FeatureWriter<'a> {

// a little helper closure used below
let name_to_gid = |name| {
self.glyph_map
.glyph_id(name)
.map(|val| GlyphId::new(val as u16))
.unwrap_or(GlyphId::NOTDEF)
self.glyph_id(name).unwrap_or(GlyphId::NOTDEF)
};

// convert the groups stored in the Kerning object into the glyph classes
Expand Down Expand Up @@ -332,21 +336,20 @@ impl<'a> FeatureWriter<'a> {
};
anchors.insert(glyph_name.clone(), glyph_anchors);
}

let mut groups: HashMap<GlyphName, MarkGroup> = Default::default();

for (glyph_name, glyph_anchors) in anchors.iter() {
// We assume the anchor list to be small
// considering only glyphs with anchors
// glyphs with *only* base anchors are bases
// glyphs with *any* mark anchor are marks
// considering only glyphs with anchors,
// - glyphs with *only* base anchors are bases
// - glyphs with *any* mark anchor are marks

let mut base = true; // TODO: only a base if user rules allow it
for anchor in glyph_anchors.anchors.iter() {
let anchor_type = anchor.name.anchor_type();
if anchor_type == AnchorType::Mark {
base = false;

// TODO: only if user rules allow us to be a mark
groups
.entry(anchor_type.group_name(anchor.name.clone()))
Expand All @@ -355,7 +358,7 @@ impl<'a> FeatureWriter<'a> {
.push(((glyph_name).clone(), anchor));
}
}

if base {
for anchor in glyph_anchors.anchors.iter() {
let anchor_type = anchor.name.anchor_type();
Expand All @@ -368,6 +371,108 @@ impl<'a> FeatureWriter<'a> {
}
}

// **** TODO: drive builder instead of generating a string ****

let mut classes = Vec::new();
let mut mark_to_bases = Vec::new();
let mut mark_to_marks = Vec::new();

for (group_name, group) in groups.iter() {
// write a mark class for every mark
for (glyph_name, mark) in group.marks.iter() {
let default_pos = mark.default_pos();
classes.push(format!(
"markClass {} <anchor {} {}> @MC{}; # TODO variable anchor",
glyph_name, default_pos.x, default_pos.y, mark.name
));
// TODO is below equivalent to above?
let mark_gid = self.glyph_id(&mark.name).unwrap_or(GlyphId::NOTDEF);
let anchor_table = AnchorTable::Format1(AnchorFormat1::new(default_pos.x.ot_round(), default_pos.y.ot_round()));
builder.define_mark_class(glyph_name.as_str(), vec![(mark_gid.into(), Some(anchor_table))]); // TODO error handling
}

// if we have bases *and* marks emit mark to base
for (base_name, base) in group.bases.iter() {
let default_pos = base.default_pos();
let mark_name = &base.name;
let lookup_name = format!("mark2base_{base_name}_{group_name}");
// TODO: arithmetic for pos
mark_to_bases.push(format!(" lookup {lookup_name} {{\n pos base {base_name} <anchor {} {}> @MC_{mark_name}; # TODO variable anchor;\n }} {lookup_name};", default_pos.x, default_pos.y));
}

// If a mark has anchors that are themselves marks what we got here is a mark to mark
let mut snippets = Vec::new();
let mut mark_names = Vec::new();
for (mark_name, mark_anchor) in group.marks.iter() {
let Some(glyph_anchors) = anchors.get(mark_name) else {
continue;
};
let base_name = AnchorType::Mark.group_name(mark_anchor.name.clone()); // TODO Ew
let Some(anchor_my_anchor) =
glyph_anchors.anchors.iter().find(|a| a.name == base_name)
else {
eprintln!("No anchor_my_anchor for {base_name}");
continue;
};
let Some((_, default_pos)) = anchor_my_anchor
.positions
.iter()
.find(|(loc, _)| !loc.has_any_non_zero())
else {
panic!("TODO return a useful error");
};
for glyph_anchor in glyph_anchors.anchors.iter() {
if AnchorType::Mark != glyph_anchor.name.anchor_type() {
continue;
}
//let group_name = AnchorType::Mark.group_name(mark_anchor.name.clone());
if snippets.is_empty() {
snippets.push(format!(" lookup mark2mark_{group_name} {{"));
snippets.push("".to_string()); // placeholder for MarkFilteringSet
snippets.push(format!(
" lookupflag UseMarkFilteringSet @MFS_mark2mark_{group_name};"
));
}
mark_names.push(mark_name.to_string());

// TODO: arithmetic for pos
snippets.push(format!(
" pos mark {} <anchor {} {}> mark @MC_{group_name}; # TODO variable anchor",
mark_name, default_pos.x, default_pos.y
));
}
}

if !snippets.is_empty() {
mark_names.sort();
snippets[1] = format!(
" @MFS_mark2mark_{group_name} = [{}];",
mark_names.join(" ")
);
snippets.push(" }".to_string());
mark_to_marks.push(snippets.join("\n"));
}
}

classes.sort();
for class in classes {
eprintln!("{class}");
}

mark_to_bases.sort();
eprintln!("\nfeature mark {{");
for mark_to_base in mark_to_bases {
eprintln!("{mark_to_base}");
}
eprintln!("}} mark;");

mark_to_marks.sort();
eprintln!("\nfeature mkmk {{");
for mark_to_mark in mark_to_marks {
eprintln!("{mark_to_mark}");
}
eprintln!("}} mkmk;");

todo!("Add marks")
}

Expand Down Expand Up @@ -435,7 +540,7 @@ impl<'a> FeatureWriter<'a> {
impl<'a> FeatureProvider for FeatureWriter<'a> {
fn add_features(&self, builder: &mut FeatureBuilder) {
self.add_kerning_features(builder);
self.add_marks(builder).unwrap(); // TODO where my error handling
self.add_marks(builder).unwrap(); // TODO where my error handling
}
}

Expand Down Expand Up @@ -580,7 +685,8 @@ impl FeatureWork {
raw_anchors: &Vec<(FeWorkId, Arc<GlyphAnchors>)>,
) -> Result<Compilation, Error> {
let var_info = FeaVariationInfo::new(static_metadata);
let feature_writer = FeatureWriter::new(static_metadata, kerning, &glyph_order, &raw_anchors);
let feature_writer =
FeatureWriter::new(static_metadata, kerning, &glyph_order, &raw_anchors);
let fears_glyph_map = create_glyphmap(glyph_order);
let compiler = match features {
Features::File {
Expand Down Expand Up @@ -675,7 +781,7 @@ impl Work<Context, AnyWorkId, Error> for FeatureWork {
let kerning = context.ir.kerning.get();
let anchors = context.ir.anchors.all();

let features = (*context.ir.features.get()).clone(); // TODO does this need to be cloned?
let features = (*context.ir.features.get()).clone(); // TODO does this need to be cloned?

if !matches!(features, Features::Empty) {
if log::log_enabled!(log::Level::Trace) {
Expand All @@ -684,7 +790,13 @@ impl Work<Context, AnyWorkId, Error> for FeatureWork {
}
}

let result = self.compile(&static_metadata, &features, &glyph_order, &kerning, &anchors);
let result = self.compile(
&static_metadata,
&features,
&glyph_order,
&kerning,
&anchors,
);
if result.is_err() || context.flags.contains(Flags::EMIT_DEBUG) {
if let Features::Memory { fea_content, .. } = &features {
write_debug_fea(context, result.is_err(), "compile failed", fea_content);
Expand Down
10 changes: 10 additions & 0 deletions fontir/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,16 @@ pub struct Anchor {
pub positions: HashMap<NormalizedLocation, Point>,
}

impl Anchor {
pub fn default_pos(&self) -> Point {
self.positions
.iter()
.find(|(loc, _)| !loc.has_any_non_zero())
.map(|(_, p)| p.clone())
.unwrap()
}
}

#[derive(Debug, Clone)]
pub struct AnchorBuilder {
glyph_name: GlyphName,
Expand Down

0 comments on commit 75d75e5

Please sign in to comment.