use fontdrasil::orchestration::{Access, AccessBuilder, Work};
use fontir::orchestration::WorkId as FeWorkId;
use log::trace;
use write_fonts::{
OtRound, dump_table,
tables::{vhea::Vhea, vmtx::Vmtx},
types::FWord,
};
use crate::{
error::Error,
metrics_and_limits::MetricsBuilder,
orchestration::{AnyWorkId, BeWork, Context, WorkId},
};
#[derive(Debug)]
struct VerticalMetricsWork {}
pub fn create_vertical_metrics_work() -> Box<BeWork> {
Box::new(VerticalMetricsWork {})
}
impl Work<Context, AnyWorkId, Error> for VerticalMetricsWork {
fn id(&self) -> AnyWorkId {
WorkId::Vmtx.into()
}
fn read_access(&self) -> Access<AnyWorkId> {
AccessBuilder::new()
.variant(FeWorkId::GlobalMetrics)
.variant(FeWorkId::GlyphOrder)
.variant(FeWorkId::ALL_GLYPHS)
.variant(WorkId::ALL_GLYF_FRAGMENTS)
.variant(WorkId::Glyf)
.build()
}
fn write_access(&self) -> Access<AnyWorkId> {
AccessBuilder::new()
.variant(WorkId::Vmtx)
.variant(WorkId::Vhea)
.build()
}
fn also_completes(&self) -> Vec<AnyWorkId> {
vec![WorkId::Vhea.into()]
}
fn exec(&self, context: &Context) -> Result<(), Error> {
let static_metadata = context.ir.static_metadata.get();
if !static_metadata.build_vertical {
trace!("Skip vmtx and vhea; this is not a vertical font");
return Ok(());
}
let glyph_order = context.ir.glyph_order.get();
let default_metrics = context
.ir
.global_metrics
.get()
.at(static_metadata.default_location());
let builder =
glyph_order
.iter()
.fold(MetricsBuilder::default(), |mut builder, (_gid, gn)| {
let glyph = context.ir.get_glyph(gn.clone());
let instance = glyph.default_instance();
let advance = instance.height(&default_metrics);
let vertical_origin = instance.vertical_origin(&default_metrics);
let glyph = context.glyphs.get(&WorkId::GlyfFragment(gn.clone()).into());
let side_bearing = vertical_origin
- glyph.data.bbox().map(|bbox| bbox.y_max).unwrap_or_default();
let bounds_advance = glyph
.data
.bbox()
.map(|bbox| bbox.y_max as i32 - bbox.y_min as i32);
builder.update(advance, side_bearing, bounds_advance);
builder
});
let metrics = builder.build();
let vhea = Vhea {
ascender: FWord::new(default_metrics.vhea_ascender.into_inner().ot_round()),
descender: FWord::new(default_metrics.vhea_descender.into_inner().ot_round()),
line_gap: FWord::new(default_metrics.vhea_line_gap.into_inner().ot_round()),
advance_height_max: metrics.advance_max,
min_top_side_bearing: metrics.min_first_side_bearing,
min_bottom_side_bearing: metrics.min_second_side_bearing,
y_max_extent: metrics.max_extent,
caret_slope_rise: default_metrics
.vhea_caret_slope_rise
.into_inner()
.ot_round(),
caret_slope_run: default_metrics.vhea_caret_slope_run.into_inner().ot_round(),
caret_offset: default_metrics.vhea_caret_offset.into_inner().ot_round(),
number_of_long_ver_metrics: metrics.long_metrics.len().try_into().map_err(|_| {
Error::OutOfBounds {
what: "number_of_long_metrics".into(),
value: format!("{}", metrics.long_metrics.len()),
}
})?,
};
context.vhea.set(vhea);
let vmtx = Vmtx::new(metrics.long_metrics, metrics.first_side_bearings);
let raw_vmtx = dump_table(&vmtx)
.map_err(|e| Error::DumpTableError {
e,
context: "vmtx".into(),
})?
.into();
context.vmtx.set(raw_vmtx);
Ok(())
}
}