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

Housekeeping: let work tell us dependencies and what it reads/writes #359

Merged
merged 2 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ parking_lot = "0.12.1"
fea-rs = "0.9.0"
font-types = { version = "0.3.2", features = ["serde"] }
read-fonts = "0.7.0"
write-fonts = "0.10.0"
write-fonts = "0.10.1"
skrifa = "0.6.0"

bitflags = "2.0"
Expand Down
37 changes: 23 additions & 14 deletions fontbe/src/avar.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
//! Generates a [avar](https://learn.microsoft.com/en-us/typography/opentype/spec/avar) table.

use font_types::F2Dot14;
use fontdrasil::orchestration::Work;
use fontdrasil::orchestration::{Access, Work};
use fontir::{
coords::{CoordConverter, DesignCoord},
ir::Axis,
orchestration::WorkId as FeWorkId,
};
use log::debug;
use write_fonts::tables::avar::{Avar, AxisValueMap, SegmentMaps};

use crate::{
error::Error,
orchestration::{BeWork, Context, WorkId},
orchestration::{AnyWorkId, BeWork, Context, WorkId},
};

#[derive(Debug)]
struct AvarWork {}

pub fn create_avar_work() -> Box<BeWork> {
Expand Down Expand Up @@ -58,29 +60,36 @@ fn to_segment_map(axis: &Axis) -> SegmentMaps {
SegmentMaps::new(mappings)
}

impl Work<Context, WorkId, Error> for AvarWork {
fn id(&self) -> WorkId {
WorkId::Avar
impl Work<Context, AnyWorkId, Error> for AvarWork {
fn id(&self) -> AnyWorkId {
WorkId::Avar.into()
}

fn read_access(&self) -> Access<AnyWorkId> {
Access::One(FeWorkId::StaticMetadata.into())
}

/// Generate [avar](https://learn.microsoft.com/en-us/typography/opentype/spec/avar)
///
/// See also <https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#CSN>
fn exec(&self, context: &Context) -> Result<(), Error> {
let static_metadata = context.ir.get_init_static_metadata();
let static_metadata = context.ir.static_metadata.get();
// Guard clause: don't produce avar for a static font
if static_metadata.variable_axes.is_empty() {
debug!("Skip avar; this is not a variable font");
return Ok(());
}
context.set_avar(Avar::new(
static_metadata
.variable_axes
.iter()
.map(to_segment_map)
.filter(|sm| !sm.axis_value_maps.is_empty())
.collect(),
));
context.avar.set_unconditionally(
Avar::new(
static_metadata
.variable_axes
.iter()
.map(to_segment_map)
.filter(|sm| !sm.axis_value_maps.is_empty())
.collect(),
)
.into(),
);
Ok(())
}
}
Expand Down
33 changes: 23 additions & 10 deletions fontbe/src/cmap.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
//! Generates a [cmap](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap) table.

use fontdrasil::orchestration::Work;
use std::sync::Arc;

use fontdrasil::orchestration::{Access, Work};
use fontir::orchestration::WorkId as FeWorkId;

use read_fonts::types::GlyphId;
use write_fonts::tables::cmap::Cmap;

use crate::{
error::Error,
orchestration::{BeWork, Context, WorkId},
orchestration::{AnyWorkId, BeWork, Context, WorkId},
};

#[derive(Debug)]
struct CmapWork {}

pub fn create_cmap_work() -> Box<BeWork> {
Box::new(CmapWork {})
}

impl Work<Context, WorkId, Error> for CmapWork {
fn id(&self) -> WorkId {
WorkId::Cmap
impl Work<Context, AnyWorkId, Error> for CmapWork {
fn id(&self) -> AnyWorkId {
WorkId::Cmap.into()
}

fn read_access(&self) -> Access<AnyWorkId> {
Access::Custom(Arc::new(|id| {
matches!(
id,
AnyWorkId::Fe(FeWorkId::GlyphOrder) | AnyWorkId::Fe(FeWorkId::Glyph(..))
)
}))
}

/// Generate [cmap](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap)
fn exec(&self, context: &Context) -> Result<(), Error> {
// cmap only accomodates single codepoint : glyph mappings; collect all of those
let static_metadata = context.ir.get_final_static_metadata();
let glyph_order = context.ir.glyph_order.get();

let mappings = static_metadata
.glyph_order
let mappings = glyph_order
.iter()
.map(|glyph_name| context.ir.get_glyph_ir(glyph_name))
.map(|glyph_name| context.ir.glyphs.get(&FeWorkId::Glyph(glyph_name.clone())))
.enumerate()
.flat_map(|(gid, glyph)| {
glyph
Expand All @@ -44,7 +57,7 @@ impl Work<Context, WorkId, Error> for CmapWork {
});

let cmap = Cmap::from_mappings(mappings);
context.set_cmap(cmap);
context.cmap.set_unconditionally(cmap.into());
Ok(())
}
}
45 changes: 32 additions & 13 deletions fontbe/src/features.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Feature binary compilation.

use std::{
collections::HashSet,
ffi::{OsStr, OsString},
fmt::Display,
fs,
Expand All @@ -12,16 +13,20 @@ use fea_rs::{
parse::{SourceLoadError, SourceResolver},
Compiler, GlyphMap, GlyphName as FeaRsGlyphName,
};
use fontir::{ir::Features, orchestration::Flags};
use fontir::{
ir::Features,
orchestration::{Flags, WorkId as FeWorkId},
};
use log::{debug, error, warn};

use fontdrasil::orchestration::Work;
use fontdrasil::orchestration::{Access, Work};

use crate::{
error::Error,
orchestration::{BeWork, Context, WorkId},
orchestration::{AnyWorkId, BeWork, Context, WorkId},
};

#[derive(Debug)]
pub struct FeatureWork {}

// I did not want to make a struct
Expand Down Expand Up @@ -111,19 +116,26 @@ fn write_debug_fea(context: &Context, is_error: bool, why: &str, fea_content: &s
};
}

impl Work<Context, WorkId, Error> for FeatureWork {
fn id(&self) -> WorkId {
WorkId::Features
impl Work<Context, AnyWorkId, Error> for FeatureWork {
fn id(&self) -> AnyWorkId {
WorkId::Features.into()
}

fn read_access(&self) -> Access<AnyWorkId> {
Access::Set(HashSet::from([
FeWorkId::Features.into(),
FeWorkId::GlyphOrder.into(),
]))
}

fn also_completes(&self) -> Vec<WorkId> {
vec![WorkId::Gpos, WorkId::Gsub]
fn also_completes(&self) -> Vec<AnyWorkId> {
vec![WorkId::Gpos.into(), WorkId::Gsub.into()]
}

fn exec(&self, context: &Context) -> Result<(), Error> {
let features = context.ir.get_features();
let features = context.ir.features.get();
if !matches!(*features, Features::Empty) {
let glyph_order = &context.ir.get_final_static_metadata().glyph_order;
let glyph_order = &context.ir.glyph_order.get();
if glyph_order.is_empty() {
warn!("Glyph order is empty; feature compile improbable");
}
Expand All @@ -146,17 +158,24 @@ impl Work<Context, WorkId, Error> for FeatureWork {
result.gsub.is_some()
);
if let Some(gpos) = result.gpos {
context.set_gpos(gpos);
context.gpos.set_unconditionally(gpos.into());
}
if let Some(gsub) = result.gsub {
context.set_gsub(gsub);
context.gsub.set_unconditionally(gsub.into());
}
} else {
debug!("No fea file, dull compile");
}
// Enables the assumption that if the file exists features were compiled
if context.flags.contains(Flags::EMIT_IR) {
fs::write(context.paths.target_file(&WorkId::Features), "1").map_err(Error::IoError)?;
fs::write(
context
.persistent_storage
.paths
.target_file(&WorkId::Features),
"1",
)
.map_err(Error::IoError)?;
}
Ok(())
}
Expand Down
110 changes: 67 additions & 43 deletions fontbe/src/font.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Merge tables into a font

use fontdrasil::orchestration::Work;
use std::collections::HashSet;

use fontdrasil::orchestration::{Access, Work};
use fontir::orchestration::WorkId as FeWorkId;
use log::debug;
use read_fonts::{
tables::{
Expand All @@ -15,9 +18,10 @@ use write_fonts::FontBuilder;

use crate::{
error::Error,
orchestration::{to_bytes, BeWork, Bytes, Context, WorkId},
orchestration::{to_bytes, AnyWorkId, BeWork, Context, WorkId},
};

#[derive(Debug)]
struct FontWork {}

pub fn create_font_work() -> Box<BeWork> {
Expand Down Expand Up @@ -50,52 +54,76 @@ const TABLES_TO_MERGE: &[(WorkId, Tag, TableType)] = &[

fn has(context: &Context, id: WorkId) -> bool {
match id {
WorkId::Avar => context.has_avar(),
WorkId::Cmap => context.has_cmap(),
WorkId::Fvar => context.has_fvar(),
WorkId::Head => context.has_head(),
WorkId::Hhea => context.has_hhea(),
WorkId::Hmtx => context.has_hmtx(),
WorkId::Glyf => context.has_glyf_loca(),
WorkId::Gpos => context.has_gpos(),
WorkId::Gsub => context.has_gsub(),
WorkId::Gvar => context.has_gvar(),
WorkId::Loca => context.has_glyf_loca(),
WorkId::Maxp => context.has_maxp(),
WorkId::Name => context.has_name(),
WorkId::Os2 => context.has_os2(),
WorkId::Post => context.has_post(),
WorkId::Stat => context.has_stat(),
WorkId::Avar => context.avar.try_get().is_some(),
WorkId::Cmap => context.cmap.try_get().is_some(),
WorkId::Fvar => context.fvar.try_get().is_some(),
WorkId::Head => context.head.try_get().is_some(),
WorkId::Hhea => context.hhea.try_get().is_some(),
WorkId::Hmtx => context.hmtx.try_get().is_some(),
WorkId::Glyf => context.glyf.try_get().is_some(),
WorkId::Gpos => context.gpos.try_get().is_some(),
WorkId::Gsub => context.gsub.try_get().is_some(),
WorkId::Gvar => context.gvar.try_get().is_some(),
WorkId::Loca => context.loca.try_get().is_some(),
WorkId::Maxp => context.maxp.try_get().is_some(),
WorkId::Name => context.name.try_get().is_some(),
WorkId::Os2 => context.os2.try_get().is_some(),
WorkId::Post => context.post.try_get().is_some(),
WorkId::Stat => context.stat.try_get().is_some(),
_ => false,
}
}

fn bytes_for(context: &Context, id: WorkId) -> Result<Vec<u8>, Error> {
// TODO: to_vec copies :(
let bytes = match id {
WorkId::Avar => to_bytes(context.get_avar().as_ref()),
WorkId::Cmap => to_bytes(&*context.get_cmap()),
WorkId::Fvar => to_bytes(&*context.get_fvar()),
WorkId::Head => to_bytes(&*context.get_head()),
WorkId::Hhea => to_bytes(&*context.get_hhea()),
WorkId::Hmtx => context.get_hmtx().get().to_vec(),
WorkId::Glyf => context.get_glyf_loca().glyf.clone(),
WorkId::Gpos => to_bytes(&*context.get_gpos()),
WorkId::Gsub => to_bytes(&*context.get_gsub()),
WorkId::Gvar => context.get_gvar().get().to_vec(),
WorkId::Loca => context.get_glyf_loca().raw_loca.clone(),
WorkId::Maxp => to_bytes(&*context.get_maxp()),
WorkId::Name => to_bytes(&*context.get_name()),
WorkId::Os2 => to_bytes(&*context.get_os2()),
WorkId::Post => to_bytes(&*context.get_post()),
WorkId::Stat => to_bytes(&*context.get_stat()),
WorkId::Avar => to_bytes(context.avar.get().as_ref()),
WorkId::Cmap => to_bytes(context.cmap.get().as_ref()),
WorkId::Fvar => to_bytes(context.fvar.get().as_ref()),
WorkId::Head => to_bytes(context.head.get().as_ref()),
WorkId::Hhea => to_bytes(context.hhea.get().as_ref()),
WorkId::Hmtx => context.hmtx.get().as_ref().get().to_vec(),
WorkId::Glyf => context.glyf.get().as_ref().get().to_vec(),
WorkId::Gpos => to_bytes(context.gpos.get().as_ref()),
WorkId::Gsub => to_bytes(context.gsub.get().as_ref()),
WorkId::Gvar => context.gvar.get().as_ref().get().to_vec(),
WorkId::Loca => context.loca.get().as_ref().get().to_vec(),
WorkId::Maxp => to_bytes(context.maxp.get().as_ref()),
WorkId::Name => to_bytes(context.name.get().as_ref()),
WorkId::Os2 => to_bytes(context.os2.get().as_ref()),
WorkId::Post => to_bytes(context.post.get().as_ref()),
WorkId::Stat => to_bytes(context.stat.get().as_ref()),
_ => panic!("Missing a match for {id:?}"),
};
Ok(bytes)
}

impl Work<Context, WorkId, Error> for FontWork {
fn id(&self) -> WorkId {
WorkId::Font
impl Work<Context, AnyWorkId, Error> for FontWork {
fn id(&self) -> AnyWorkId {
WorkId::Font.into()
}

fn read_access(&self) -> Access<AnyWorkId> {
Access::Set(HashSet::from([
WorkId::Avar.into(),
WorkId::Cmap.into(),
WorkId::Fvar.into(),
WorkId::Head.into(),
WorkId::Hhea.into(),
WorkId::Hmtx.into(),
WorkId::Glyf.into(),
WorkId::Gpos.into(),
WorkId::Gsub.into(),
WorkId::Gvar.into(),
WorkId::Loca.into(),
WorkId::Maxp.into(),
WorkId::Name.into(),
WorkId::Os2.into(),
WorkId::Post.into(),
WorkId::Stat.into(),
FeWorkId::StaticMetadata.into(),
WorkId::LocaFormat.into(),
]))
}

/// Glue binary tables into a font
Expand All @@ -104,11 +132,7 @@ impl Work<Context, WorkId, Error> for FontWork {
let mut builder = FontBuilder::default();

// A fancier implementation would mmap the files. We basic.
let is_static = context
.ir
.get_init_static_metadata()
.variable_axes
.is_empty();
let is_static = context.ir.static_metadata.get().variable_axes.is_empty();
for (work_id, tag, table_type) in TABLES_TO_MERGE {
if is_static && matches!(table_type, TableType::Variable) {
debug!("Skip {tag} because this is a static font");
Expand All @@ -126,7 +150,7 @@ impl Work<Context, WorkId, Error> for FontWork {
debug!("Building font");
let font = builder.build();
debug!("Assembled {} byte font", font.len());
context.set_font(Bytes::new(font));
context.font.set_unconditionally(font.into());
Ok(())
}
}
Loading