Skip to content

Commit

Permalink
Merge pull request #359 from googlefonts/deps3
Browse files Browse the repository at this point in the history
Housekeeping: let work tell us dependencies and what it reads/writes
  • Loading branch information
rsheeter authored Jul 7, 2023
2 parents 55f5b70 + 5ebba04 commit 376a425
Show file tree
Hide file tree
Showing 31 changed files with 2,228 additions and 1,848 deletions.
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

0 comments on commit 376a425

Please sign in to comment.