use crate::font::{Font, Part};
use crate::Error;
use super::{glyph_id, k, Item, MathBox, Style};
type R<T> = Result<T, Error>;
pub fn vertical(c: char, target: f32, e: Style) -> R<MathBox> {
let f = e.font();
let s = e.scale();
let id = f.glyph(c).ok_or(Error::MissingGlyph { character: c })?;
let base = glyph_id(id, s, f);
if base.total() >= target {
return Ok(base);
}
let variants = f.variants(id, true);
for (v, measure) in &variants {
if measure * s >= target {
return Ok(glyph_id(*v, s, f));
}
}
if let Some(parts) = f.assembly(id, true) {
return Ok(assemble(&parts, target / s, s, true, f));
}
Ok(variants.last().map_or(base, |(v, _)| glyph_id(*v, s, f)))
}
pub fn horizontal(c: char, target: f32, e: Style) -> R<MathBox> {
let f = e.font();
let s = e.scale();
let id = f.glyph(c).ok_or(Error::MissingGlyph { character: c })?;
let width = |g| {
let (x0, _, x1, _) = f.limits(g);
(x1 - x0) * s
};
if width(id) >= target {
return Ok(glyph_id(id, s, f));
}
let variants = f.variants(id, false);
for (v, _) in &variants {
if width(*v) >= target {
return Ok(glyph_id(*v, s, f));
}
}
if let Some(parts) = f.assembly(id, false) {
return Ok(assemble(&parts, target / s, s, false, f));
}
Ok(variants.last().map_or(glyph_id(id, s, f), |(v, _)| glyph_id(*v, s, f)))
}
pub fn delimiter(c: char, target: f32, e: Style) -> R<MathBox> {
let mut math_box = vertical(c, target, e)?;
let axis = k(e, |c| c.axis_height());
let offset = axis - (math_box.height - math_box.depth) / 2.0;
math_box.shift_vertical(offset);
math_box.single_glyph = false;
Ok(math_box)
}
fn assemble(parts: &[Part], target: f32, s: f32, vertical: bool, f: &Font) -> MathBox {
let overlap = f.min_overlap();
let sequence = |n: usize| -> Vec<Part> { parts.iter().flat_map(|p| std::iter::repeat_n(*p, if p.extender { n } else { 1 })).collect() };
let size = |seq: &[Part], ov: f32| -> f32 { seq.iter().map(|p| p.advance).sum::<f32>() - ov * seq.len().saturating_sub(1) as f32 };
let mut n = 0;
let mut seq = sequence(n);
while size(&seq, overlap) < target && n < 200 {
n += 1;
seq = sequence(n);
}
let joined = seq.len().saturating_sub(1).max(1) as f32;
let slack = seq.windows(2).map(|w| w[0].end.min(w[1].start)).fold(f32::INFINITY, f32::min);
let excess = (size(&seq, overlap) - target).max(0.0);
let ov = (overlap + excess / joined).min(slack.max(overlap));
let mut math_box = MathBox::empty();
let mut cursor = 0.0;
for p in &seq {
let (x0, y0, _, _) = f.limits(p.id);
let (x, y) = if vertical { (0.0, (cursor - y0) * s) } else { ((cursor - x0) * s, 0.0) };
math_box.items.push(Item::Glyph { id: p.id, x, y, scale: s, color: None });
let g = glyph_id(p.id, s, f);
if vertical {
math_box.width = math_box.width.max(g.width);
} else {
math_box.height = math_box.height.max(g.height);
math_box.depth = math_box.depth.max(g.depth);
}
cursor += p.advance - ov;
}
let total = (cursor + ov) * s;
if vertical {
math_box.height = total;
math_box.depth = 0.0;
} else {
math_box.width = total;
}
math_box
}