Skip to content

Commit 0463a58

Browse files
committed
Make nested components fix depth first (still other problems to fix here)
1 parent 356559e commit 0463a58

File tree

2 files changed

+93
-54
lines changed

2 files changed

+93
-54
lines changed

profile-universal/src/checks/nested_components.rs

+31-19
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
use std::collections::HashMap;
22

33
use fontspector_checkapi::{fixfont, prelude::*, testfont, FileTypeConvert};
4-
use read_fonts::{tables::glyf::Glyph, TableProvider};
4+
use read_fonts::{
5+
tables::{
6+
glyf::{Glyf, Glyph},
7+
loca::Loca,
8+
},
9+
TableProvider,
10+
};
511
use skrifa::GlyphId;
6-
use write_fonts::tables::glyf::CompositeGlyph;
712

813
use super::transformed_components::decompose_components_impl;
914

@@ -64,6 +69,19 @@ fn nested_components(f: &Testable, context: &Context) -> CheckFnResult {
6469
}
6570
}
6671

72+
fn get_depth(glyph_id: GlyphId, loca: &Loca, glyf: &Glyf) -> u32 {
73+
let mut depth = 0;
74+
let glyph_entry = loca.get_glyf(glyph_id, glyf).ok().flatten();
75+
if let Some(Glyph::Composite(composite)) = glyph_entry {
76+
depth = 1 + composite
77+
.components()
78+
.map(|component| get_depth(component.glyph.into(), loca, glyf))
79+
.max()
80+
.unwrap_or(0)
81+
}
82+
depth
83+
}
84+
6785
fn decompose_nested_components(t: &Testable) -> FixFnResult {
6886
let font = fixfont!(t);
6987
let loca = font
@@ -74,22 +92,16 @@ fn decompose_nested_components(t: &Testable) -> FixFnResult {
7492
.font()
7593
.glyf()
7694
.map_err(|_| "glyf table not found".to_string())?;
77-
let composite_glyphs: HashMap<GlyphId, _> = font
78-
.all_glyphs()
79-
.filter_map(|glyphid| {
80-
if let Some(Glyph::Composite(composite)) = loca.get_glyf(glyphid, &glyf).ok()? {
81-
Some((glyphid, composite))
82-
} else {
83-
None
84-
}
85-
})
86-
.collect();
95+
let mut depths = HashMap::new();
96+
for glyph in font.all_glyphs() {
97+
depths.insert(glyph, get_depth(glyph, &loca, &glyf));
98+
}
99+
// Drop all with depth <2
100+
depths.retain(|_, depth| *depth > 1);
101+
// Sort by depth, descending
102+
let mut sorted_glyphs: Vec<GlyphId> = depths.keys().copied().collect();
103+
sorted_glyphs.sort_by_key(|&glyph| depths[&glyph]);
104+
sorted_glyphs.reverse();
87105

88-
let has_nested_components = |composite: &CompositeGlyph| {
89-
composite
90-
.components()
91-
.iter()
92-
.any(|component| composite_glyphs.contains_key(&component.glyph.into()))
93-
};
94-
decompose_components_impl(t, has_nested_components)
106+
decompose_components_impl(t, &sorted_glyphs)
95107
}

profile-universal/src/checks/transformed_components.rs

+62-35
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use fontspector_checkapi::{fixfont, prelude::*, testfont, FileTypeConvert};
2+
use hashbrown::HashMap;
3+
use itertools::Itertools;
24
use kurbo::Affine;
35
use read_fonts::{
46
tables::glyf::{Anchor, CurvePoint, Glyph, Transform},
57
types::F2Dot14,
68
FontData, TableProvider,
79
};
8-
use skrifa::GlyphId16;
10+
use skrifa::GlyphId;
911
use write_fonts::{
1012
from_obj::ToOwnedObj,
1113
tables::glyf::{
@@ -98,20 +100,40 @@ fn transformed_components(f: &Testable, context: &Context) -> CheckFnResult {
98100
}
99101
}
100102

101-
fn bad_composite(c: &CompositeGlyph) -> bool {
102-
c.components().iter().any(|component| {
103-
!transform_is_linear(component.transform) || transform_is_semi_flipped(component.transform)
104-
})
105-
}
106-
107103
fn decompose_transformed_components(t: &Testable) -> FixFnResult {
108-
decompose_components_impl(t, bad_composite)
104+
let f = fixfont!(t);
105+
let loca = f
106+
.font()
107+
.loca(None)
108+
.map_err(|_| "loca table not found".to_string())?;
109+
let glyf = f
110+
.font()
111+
.glyf()
112+
.map_err(|_| "glyf table not found".to_string())?;
113+
let bad_composites = f
114+
.all_glyphs()
115+
.filter_map(|gid| {
116+
loca.get_glyf(gid, &glyf)
117+
.ok()
118+
.flatten()
119+
.and_then(|glyph| match glyph {
120+
Glyph::Composite(composite)
121+
if composite.components().any(|component| {
122+
!transform_is_linear(component.transform)
123+
|| transform_is_semi_flipped(component.transform)
124+
}) =>
125+
{
126+
Some(gid)
127+
}
128+
_ => None,
129+
})
130+
})
131+
.collect::<Vec<_>>();
132+
133+
decompose_components_impl(t, &bad_composites)
109134
}
110135

111-
pub(crate) fn decompose_components_impl(
112-
t: &Testable,
113-
bad_composite: impl Fn(&CompositeGlyph) -> bool,
114-
) -> FixFnResult {
136+
pub(crate) fn decompose_components_impl(t: &Testable, decompose_order: &[GlyphId]) -> FixFnResult {
115137
let f = fixfont!(t);
116138
let mut new_font = FontBuilder::new();
117139
let mut builder = GlyfLocaBuilder::new();
@@ -123,31 +145,36 @@ pub(crate) fn decompose_components_impl(
123145
.font()
124146
.glyf()
125147
.map_err(|_| "glyf table not found".to_string())?;
126-
let all_glyphs: Vec<WriteGlyph> = f
148+
let mut all_glyphs: HashMap<GlyphId, WriteGlyph> = f
127149
.all_glyphs()
128150
.map(|gid| {
129-
loca.get_glyf(gid, &glyf).map(|option_glyph| {
130-
option_glyph
131-
.map(|glyph| {
132-
let g: WriteGlyph = glyph.to_owned_obj(FontData::new(&[]));
133-
g
134-
})
135-
.unwrap_or(WriteGlyph::Empty)
136-
})
151+
loca.get_glyf(gid, &glyf)
152+
.map(|option_glyph| {
153+
option_glyph
154+
.map(|glyph| {
155+
let g: WriteGlyph = glyph.to_owned_obj(FontData::new(&[]));
156+
g
157+
})
158+
.unwrap_or(WriteGlyph::Empty)
159+
})
160+
.map(|x| (gid, x))
137161
})
138-
.collect::<Result<Vec<WriteGlyph>, _>>()
162+
.collect::<Result<HashMap<GlyphId, WriteGlyph>, _>>()
139163
.map_err(|x| x.to_string())?;
140-
for glyph in all_glyphs.iter() {
141-
match glyph {
142-
WriteGlyph::Composite(composite) if bad_composite(composite) => {
164+
for glyph_id in decompose_order {
165+
let current_glyph = all_glyphs.get(glyph_id).ok_or("glyph not found")?;
166+
match current_glyph {
167+
WriteGlyph::Composite(composite) => {
143168
let new_glyph = decompose_glyph(composite, &all_glyphs)?;
144-
builder.add_glyph(&new_glyph).map_err(|x| x.to_string())?;
145-
}
146-
WriteGlyph::Composite(_) | WriteGlyph::Empty | WriteGlyph::Simple(_) => {
147-
builder.add_glyph(glyph).map_err(|x| x.to_string())?;
169+
all_glyphs.insert(*glyph_id, new_glyph);
148170
}
171+
WriteGlyph::Empty | WriteGlyph::Simple(_) => {}
149172
}
150173
}
174+
for glyph_id in all_glyphs.keys().sorted_by(|a, b| a.cmp(b)) {
175+
let glyph = all_glyphs.get(glyph_id).ok_or("glyph not found")?;
176+
builder.add_glyph(glyph).map_err(|x| x.to_string())?;
177+
}
151178
let (new_glyph, new_loca, _head_format) = builder.build();
152179
new_font.add_table(&new_glyph).map_err(|x| x.to_string())?;
153180
new_font.add_table(&new_loca).map_err(|x| x.to_string())?;
@@ -160,12 +187,12 @@ pub(crate) fn decompose_components_impl(
160187

161188
fn decompose_glyph(
162189
composite: &CompositeGlyph,
163-
glyphs: &[WriteGlyph],
190+
glyphs: &HashMap<GlyphId, WriteGlyph>,
164191
) -> Result<WriteGlyph, String> {
165192
let mut new_glyph = SimpleGlyph::default();
166193
for component in composite.components() {
167194
for (gid, affine) in flatten_component(glyphs, component)? {
168-
let component_glyph = glyphs.get(gid.to_u16() as usize).ok_or("glyph not found")?;
195+
let component_glyph = glyphs.get(&gid).ok_or("glyph not found")?;
169196
match component_glyph {
170197
WriteGlyph::Simple(simple) => {
171198
new_glyph
@@ -193,16 +220,16 @@ fn transform_contour(c: &Contour, affine: Affine) -> Contour {
193220
}
194221

195222
fn flatten_component(
196-
glyphs: &[WriteGlyph],
223+
glyphs: &HashMap<GlyphId, WriteGlyph>,
197224
component: &Component,
198-
) -> Result<Vec<(GlyphId16, kurbo::Affine)>, String> {
225+
) -> Result<Vec<(GlyphId, kurbo::Affine)>, String> {
199226
let glyph = glyphs
200-
.get(component.glyph.to_u16() as usize)
227+
.get(&GlyphId::from(component.glyph))
201228
.ok_or("glyph not found")?;
202229
Ok(match glyph {
203230
WriteGlyph::Empty => vec![],
204231
WriteGlyph::Simple(_) => vec![(
205-
component.glyph,
232+
component.glyph.into(),
206233
to_kurbo_transform(&component.transform, &component.anchor),
207234
)],
208235
WriteGlyph::Composite(composite_glyph) => {

0 commit comments

Comments
 (0)