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

Use fontations glyf/loca builder #383

Merged
merged 1 commit into from
Aug 11, 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
48 changes: 18 additions & 30 deletions fontbe/src/glyphs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ use read_fonts::{
};
use write_fonts::{
tables::{
glyf::{Bbox, Component, ComponentFlags, CompositeGlyph, Glyph as RawGlyph, SimpleGlyph},
glyf::{
Bbox, Component, ComponentFlags, CompositeGlyph, GlyfLocaBuilder, Glyph as RawGlyph,
SimpleGlyph,
},
variations::iup_delta_optimize,
},
OtRound,
};

use crate::{
error::{Error, GlyphProblem},
orchestration::{AnyWorkId, BeWork, Context, Glyph, GvarFragment, LocaFormat, WorkId},
orchestration::{AnyWorkId, BeWork, Context, Glyph, GvarFragment, WorkId},
};

#[derive(Debug)]
Expand Down Expand Up @@ -778,36 +781,21 @@ impl Work<Context, AnyWorkId, Error> for GlyfLocaWork {
compute_composite_bboxes(context)?;

let glyph_order = context.ir.glyph_order.get();
let mut builder = GlyfLocaBuilder::new();

// Glue together glyf and loca
// Build loca as u32 first, then shrink if it'll fit
// This isn't overly memory efficient but ... fonts aren't *that* big (yet?)
let mut loca = vec![0];
let mut glyf: Vec<u8> = Vec::new();
glyf.reserve(1024 * 1024); // initial size, will grow as needed
glyph_order
.iter()
.map(|gn| context.glyphs.get(&WorkId::GlyfFragment(gn.clone()).into()))
.for_each(|g| {
let bytes = g.to_bytes();
loca.push(loca.last().unwrap() + bytes.len() as u32);
glyf.extend(bytes);
});
for name in glyph_order.iter() {
let glyph = context
.glyphs
.get(&WorkId::GlyfFragment(name.clone()).into());
builder.add_glyph(&glyph.data).unwrap();
}

let loca_format = LocaFormat::new(&loca);
let loca: Vec<u8> = match loca_format {
LocaFormat::Short => loca
.iter()
.flat_map(|offset| ((offset >> 1) as u16).to_be_bytes())
.collect(),
LocaFormat::Long => loca
.iter()
.flat_map(|offset| offset.to_be_bytes())
.collect(),
};
context.loca_format.set(loca_format);
context.glyf.set(glyf.into());
context.loca.set(loca.into());
let (glyf, loca, loca_format) = builder.build();
let raw_loca = write_fonts::dump_table(&loca).unwrap();
let raw_glyf = write_fonts::dump_table(&glyf).unwrap();
context.loca_format.set(loca_format.into());
context.glyf.set(raw_glyf.into());
context.loca.set(raw_loca.into());

Ok(())
}
Expand Down
11 changes: 6 additions & 5 deletions fontbe/src/head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use font_types::{Fixed, LongDateTime};
use fontdrasil::orchestration::{Access, Work};
use fontir::orchestration::WorkId as FeWorkId;
use log::warn;
use write_fonts::tables::head::Head;
use write_fonts::tables::{head::Head, loca::LocaFormat};

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

#[derive(Debug)]
Expand Down Expand Up @@ -104,10 +104,10 @@ impl Work<Context, AnyWorkId, Error> for HeadWork {
/// Generate [head](https://learn.microsoft.com/en-us/typography/opentype/spec/head)
fn exec(&self, context: &Context) -> Result<(), Error> {
let static_metadata = context.ir.static_metadata.get();
let loca_format = context.loca_format.get();
let loca_format = (*context.loca_format.get().as_ref()).into();
let mut head = init_head(
static_metadata.units_per_em,
*loca_format,
loca_format,
static_metadata.misc.head_flags,
static_metadata.misc.lowest_rec_ppm,
);
Expand All @@ -130,8 +130,9 @@ mod tests {
use chrono::{TimeZone, Utc};
use more_asserts::assert_ge;
use temp_env;
use write_fonts::tables::loca::LocaFormat;

use crate::{head::apply_created_modified, orchestration::LocaFormat};
use crate::head::apply_created_modified;

use super::{init_head, seconds_since_mac_epoch};

Expand Down
72 changes: 23 additions & 49 deletions fontbe/src/orchestration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use write_fonts::{
dump_table,
tables::{
avar::Avar, cmap::Cmap, fvar::Fvar, glyf::Glyph as RawGlyph, gpos::Gpos, gsub::Gsub,
gvar::GlyphDeltas, head::Head, hhea::Hhea, maxp::Maxp, name::Name, os2::Os2, post::Post,
stat::Stat, variations::Tuple,
gvar::GlyphDeltas, head::Head, hhea::Hhea, loca::LocaFormat, maxp::Maxp, name::Name,
os2::Os2, post::Post, stat::Stat, variations::Tuple,
},
validate::Validate,
FontWrite, OtRound,
Expand Down Expand Up @@ -251,41 +251,39 @@ impl TupleBuilder {
}
}

#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum LocaFormat {
Short = 0,
Long = 1,
// work around orphan rules.
//
// FIXME: Clarify if there's a good reason not to treat glyf/loca as a single
// entity, for the purpose of persistence? like a struct that contains both
// tables, and from which the format can be retrieved
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that we compute them together I think this might make more sense and might be a better change. Want to try that?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also I really dislike the format wrapper; hopefully use of a single entity would let us avoid that

Copy link
Member Author

Choose a reason for hiding this comment

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

yea, it felt very bad writing this code. That said I think it makes sense to merge as is and open an issue, and that work can be a separate patch.

//
// this whole thing needs a rethink, but this gets us working
#[derive(Clone, Copy, Serialize, Deserialize, PartialEq)]
pub struct LocaFormatWrapper(u8);

impl From<LocaFormat> for LocaFormatWrapper {
fn from(value: LocaFormat) -> Self {
LocaFormatWrapper(value as _)
}
}

impl LocaFormat {
pub fn new(loca: &[u32]) -> LocaFormat {
// https://github.com/fonttools/fonttools/blob/1c283756a5e39d69459eea80ed12792adc4922dd/Lib/fontTools/ttLib/tables/_l_o_c_a.py#L37
if loca.last().copied().unwrap_or_default() < 0x20000
&& loca.iter().all(|offset| offset % 2 == 0)
{
impl From<LocaFormatWrapper> for LocaFormat {
fn from(value: LocaFormatWrapper) -> Self {
if value.0 == 0 {
LocaFormat::Short
} else {
LocaFormat::Long
}
}
}

impl Persistable for LocaFormat {
impl Persistable for LocaFormatWrapper {
fn read(from: &mut dyn Read) -> Self {
let mut buf = Vec::new();
from.read_to_end(&mut buf).unwrap();
match buf.first() {
Some(0) => LocaFormat::Short,
Some(1) => LocaFormat::Long,
_ => {
panic!("serialized LocaFormat is invalid")
}
}
bincode::deserialize_from(from).unwrap()
anthrotype marked this conversation as resolved.
Show resolved Hide resolved
}

fn write(&self, to: &mut dyn io::Write) {
to.write_all(&[*self as u8]).unwrap();
bincode::serialize_into(to, self).unwrap()
}
}

Expand Down Expand Up @@ -380,7 +378,7 @@ pub struct Context {
pub gvar: BeContextItem<Bytes>,
pub post: BeContextItem<BeValue<Post>>,
pub loca: BeContextItem<Bytes>,
pub loca_format: BeContextItem<LocaFormat>,
pub loca_format: BeContextItem<LocaFormatWrapper>,
pub maxp: BeContextItem<BeValue<Maxp>>,
pub name: BeContextItem<BeValue<Name>>,
pub os2: BeContextItem<BeValue<Os2>>,
Expand Down Expand Up @@ -518,7 +516,6 @@ where

#[cfg(test)]
mod tests {
use crate::orchestration::LocaFormat;
use font_types::Tag;
use fontir::{
coords::NormalizedCoord,
Expand All @@ -527,29 +524,6 @@ mod tests {

use super::GvarFragment;

#[test]
fn no_glyphs_is_short() {
assert_eq!(LocaFormat::Short, LocaFormat::new(&Vec::new()));
}

#[test]
fn some_glyphs_is_short() {
assert_eq!(LocaFormat::Short, LocaFormat::new(&[24, 48, 112]));
}

#[test]
fn unpadded_glyphs_is_long() {
assert_eq!(LocaFormat::Long, LocaFormat::new(&[24, 7, 112]));
}

#[test]
fn big_glyphs_is_long() {
assert_eq!(
LocaFormat::Long,
LocaFormat::new(&(0..=32).map(|i| i * 0x1000).collect::<Vec<_>>())
);
}

fn non_default_region() -> VariationRegion {
let mut region = VariationRegion::default();
region.insert(
Expand Down
12 changes: 8 additions & 4 deletions fontc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ mod tests {

use chrono::{Duration, TimeZone, Utc};
use fontbe::orchestration::{
AnyWorkId, Context as BeContext, Glyph, LocaFormat, WorkId as BeWorkIdentifier,
AnyWorkId, Context as BeContext, Glyph, LocaFormatWrapper, WorkId as BeWorkIdentifier,
};
use fontdrasil::types::GlyphName;
use fontir::{
Expand Down Expand Up @@ -344,7 +344,10 @@ mod tests {
use tempfile::{tempdir, TempDir};
use write_fonts::{
dump_table,
tables::glyf::{Bbox, Glyph as RawGlyph},
tables::{
glyf::{Bbox, Glyph as RawGlyph},
loca::LocaFormat,
},
};

use super::*;
Expand Down Expand Up @@ -397,9 +400,10 @@ mod tests {
impl Glyphs {
fn new(build_dir: &Path) -> Self {
Glyphs {
loca_format: LocaFormat::read(
loca_format: LocaFormatWrapper::read(
&mut File::open(build_dir.join("loca.format")).unwrap(),
),
)
.into(),
raw_glyf: read_file(&build_dir.join("glyf.table")),
raw_loca: read_file(&build_dir.join("loca.table")),
}
Expand Down