use crate::error::MathError;
use crate::layout::Engine;
use crate::mbox::{Layout, PathContour, PathSeg};
use crate::node::Span;
pub fn resolve_paths(engine: &Engine, layout: &Layout) -> Result<Vec<PathContour>, MathError> {
let mut out = Vec::new();
for glyph in &layout.glyphs {
let Some(font) = engine.faces().font(glyph.face) else {
return Err(MathError::UnmappedChar {
ch: glyph.ch,
span: glyph.span,
});
};
let outline = font
.glyph_outline(glyph.gid)
.map_err(|_| MathError::UnmappedChar {
ch: glyph.ch,
span: glyph.span,
})?;
let upm = f64::from(font.units_per_em.max(1));
let s = glyph.size / upm;
for contour in &outline.contours {
let mut segments = Vec::with_capacity(contour.segments.len());
let start = (glyph.x + contour.start.x * s, glyph.y + contour.start.y * s);
for seg in &contour.segments {
segments.push(match seg {
fmd_font::outline::Segment::Line { to } => PathSeg::Line {
to: (glyph.x + to.x * s, glyph.y + to.y * s),
},
fmd_font::outline::Segment::Quad { ctrl, to } => PathSeg::Quad {
ctrl: (glyph.x + ctrl.x * s, glyph.y + ctrl.y * s),
to: (glyph.x + to.x * s, glyph.y + to.y * s),
},
});
}
out.push(PathContour { start, segments });
}
}
for rule in &layout.rules {
let x0 = rule.x;
let y0 = rule.y;
let x1 = rule.x + rule.width;
let y1 = rule.y + rule.height;
out.push(PathContour {
start: (x0, y0),
segments: vec![
PathSeg::Line { to: (x1, y0) },
PathSeg::Line { to: (x1, y1) },
PathSeg::Line { to: (x0, y1) },
PathSeg::Line { to: (x0, y0) },
],
});
}
for path in &layout.paths {
out.extend(path.contours.iter().cloned());
}
Ok(out)
}
#[must_use]
pub fn canonical_dump(contours: &[PathContour]) -> String {
use core::fmt::Write as _;
let mut out = String::new();
for (i, contour) in contours.iter().enumerate() {
let _ = writeln!(
out,
"contour {} start {:.6} {:.6}",
i, contour.start.0, contour.start.1
);
for seg in &contour.segments {
match seg {
PathSeg::Line { to } => {
let _ = writeln!(out, " line {:.6} {:.6}", to.0, to.1);
}
PathSeg::Quad { ctrl, to } => {
let _ = writeln!(
out,
" quad {:.6} {:.6} {:.6} {:.6}",
ctrl.0, ctrl.1, to.0, to.1
);
}
}
}
}
out
}
#[must_use]
pub fn layout_dump(layout: &Layout) -> String {
use core::fmt::Write as _;
let mut out = String::new();
let _ = writeln!(
out,
"layout w {:.6} h {:.6} d {:.6}",
layout.width, layout.height, layout.depth
);
for g in &layout.glyphs {
let _ = writeln!(
out,
"glyph face {} gid {} ch U+{:04X} x {:.6} y {:.6} size {:.6} span {}..{}",
g.face.0, g.gid, g.ch as u32, g.x, g.y, g.size, g.span.start, g.span.end
);
}
for r in &layout.rules {
let _ = writeln!(
out,
"rule x {:.6} y {:.6} w {:.6} h {:.6} span {}..{}",
r.x, r.y, r.width, r.height, r.span.start, r.span.end
);
}
for p in &layout.paths {
let _ = writeln!(
out,
"path contours {} span {}..{}",
p.contours.len(),
p.span.start,
p.span.end
);
}
out
}
#[must_use]
pub fn spans_cover(layout: &Layout, source_len: usize) -> bool {
let ok = |s: &Span| s.end <= source_len && s.start <= s.end;
layout.glyphs.iter().all(|g| ok(&g.span))
&& layout.rules.iter().all(|r| ok(&r.span))
&& layout.paths.iter().all(|p| ok(&p.span))
}