Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fontbe/avar] don't skip no-op segment maps #586

Merged
merged 2 commits into from
Nov 21, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions fontbe/src/avar.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Generates a [avar](https://learn.microsoft.com/en-us/typography/opentype/spec/avar) table.

use font_types::F2Dot14;
use fontdrasil::{
coords::{CoordConverter, DesignCoord, NormalizedCoord},
orchestration::{Access, Work},
Expand All @@ -21,7 +22,31 @@ pub fn create_avar_work() -> Box<BeWork> {
Box::new(AvarWork {})
}

fn to_segment_map(axis: &Axis) -> Option<SegmentMaps> {
/// Return true if the SegmentMaps is an identity map
// TODO: make this a method of SegmentMaps in write-fonts
fn is_identity(segment_map: &SegmentMaps) -> bool {
segment_map
.axis_value_maps
.iter()
.all(|av| av.from_coordinate == av.to_coordinate)
}

/// Return a default avar SegmentMaps containing the required {-1:-1, 0:0, 1:1} maps
fn default_segment_map() -> SegmentMaps {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this feels like it should probably be the impl of Default for SegmentMaps; however we currently autogenerate Default impls for all write types, and there's no mechanism to override this, so this is probably fine.

// The OT avar spec would allow us to leave the axis value maps empty, however some
// implementations want the 3 required maps to always be present even when the default
// normalization for an axis was not modified.
// We are matching fontTools.varLib here:
// https://github.com/fonttools/fonttools/blob/51e70f9/Lib/fontTools/varLib/__init__.py#L151-L157
// https://learn.microsoft.com/en-us/typography/opentype/spec/avar#table-formats
SegmentMaps::new(vec![
AxisValueMap::new(F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)),
AxisValueMap::new(F2Dot14::from_f32(0.0), F2Dot14::from_f32(0.0)),
AxisValueMap::new(F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)),
])
}

fn to_segment_map(axis: &Axis) -> SegmentMaps {
// default normalization
let default_converter = CoordConverter::new(
vec![
Expand Down Expand Up @@ -57,9 +82,9 @@ fn to_segment_map(axis: &Axis) -> Option<SegmentMaps> {

// avar maps from the default normalization to the actual one,
// using normalized values on both sides.
// All identity mappings are not interesting so we can skip them.
// All identity mappings are not interesting so we return the default mapping.
if mappings.iter().all(|(k, v)| k == v) {
return None;
return default_segment_map();
}

let mappings = mappings
Expand All @@ -69,7 +94,7 @@ fn to_segment_map(axis: &Axis) -> Option<SegmentMaps> {
})
.collect();

Some(SegmentMaps::new(mappings))
SegmentMaps::new(mappings)
}

impl Work<Context, AnyWorkId, Error> for AvarWork {
Expand All @@ -91,13 +116,12 @@ impl Work<Context, AnyWorkId, Error> for AvarWork {
debug!("Skip avar; this is not a variable font");
return Ok(());
}
let axis_segment_maps: Vec<_> = static_metadata
.axes
let axis_segment_maps: Vec<_> = static_metadata.axes.iter().map(to_segment_map).collect();
// only when all the segment maps are uninteresting, we can omit avar
let avar = axis_segment_maps
.iter()
.filter_map(to_segment_map)
.filter(|sm| !sm.axis_value_maps.is_empty())
.collect();
let avar = (!axis_segment_maps.is_empty()).then_some(Avar::new(axis_segment_maps));
.any(|sm| !is_identity(sm))
.then(|| Avar::new(axis_segment_maps));
context.avar.set_unconditionally(avar.into());
Ok(())
}
Expand All @@ -113,7 +137,7 @@ mod tests {
use std::{cmp, str::FromStr};
use write_fonts::tables::avar::SegmentMaps;

use super::to_segment_map;
use super::{default_segment_map, to_segment_map};

fn axis(mappings: Vec<(UserCoord, DesignCoord)>, default_idx: usize) -> Axis {
let default_idx = cmp::min(mappings.len() - 1, default_idx);
Expand Down Expand Up @@ -150,7 +174,7 @@ mod tests {
];
for i in 1..mappings.len() {
let mappings = mappings[0..i].to_vec();
assert!(to_segment_map(&axis(mappings, 1)).is_none());
assert_eq!(to_segment_map(&axis(mappings, 1)), default_segment_map());
}
}

Expand All @@ -164,7 +188,7 @@ mod tests {
];
assert_eq!(
vec![(-1.0, -1.0), (0.0, 0.0), (0.75, 0.95), (1.0, 1.0),],
dump(to_segment_map(&axis(mappings, 1)).unwrap())
dump(to_segment_map(&axis(mappings, 1)))
);
}

Expand All @@ -179,7 +203,7 @@ mod tests {
];
assert_eq!(
vec![(-1.0, -1.0), (0.0, 0.0), (0.3333, 0.4943), (1.0, 1.0),],
dump(to_segment_map(&axis(mappings, 0)).unwrap())
dump(to_segment_map(&axis(mappings, 0)))
);
}
}