use crate::face::Face;
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PositionedGlyph {
pub glyph_id: u16,
pub x_offset: f32,
pub y_offset: f32,
pub x_advance: f32,
pub face_idx: u16,
}
#[derive(Debug)]
pub struct Shaper;
impl Shaper {
pub fn shape(face: &Face, text: &str, size_px: f32) -> Result<Vec<PositionedGlyph>, Error> {
if text.is_empty() || size_px <= 0.0 {
return Ok(Vec::new());
}
face.with_font(|font| shape_with_font(font, text, size_px))
}
}
fn shape_with_font(font: &oxideav_ttf::Font<'_>, text: &str, size_px: f32) -> Vec<PositionedGlyph> {
let raw_glyphs: Vec<u16> = text
.chars()
.map(|ch| font.glyph_index(ch).unwrap_or(0))
.collect();
shape_run_with_font(font, &raw_glyphs, size_px, 0)
}
pub fn shape_run_with_font(
font: &oxideav_ttf::Font<'_>,
raw_glyphs: &[u16],
size_px: f32,
face_idx: u16,
) -> Vec<PositionedGlyph> {
let upem = font.units_per_em().max(1) as f32;
let scale = size_px / upem;
let mut shaped_gids: Vec<u16> = Vec::with_capacity(raw_glyphs.len());
let mut i = 0;
while i < raw_glyphs.len() {
if let Some((replacement, count)) = font.lookup_ligature(&raw_glyphs[i..]) {
if count >= 2 {
shaped_gids.push(replacement);
i += count;
continue;
}
}
shaped_gids.push(raw_glyphs[i]);
i += 1;
}
let mut out: Vec<PositionedGlyph> = Vec::with_capacity(shaped_gids.len());
for (idx, &gid) in shaped_gids.iter().enumerate() {
let advance_units = font.glyph_advance(gid) as f32;
let x_advance = advance_units * scale;
let mut x_offset = 0.0_f32;
if idx > 0 {
let prev = shaped_gids[idx - 1];
let kern_units = font.lookup_kerning(prev, gid) as f32;
x_offset = kern_units * scale;
}
out.push(PositionedGlyph {
glyph_id: gid,
x_offset,
y_offset: 0.0,
x_advance,
face_idx,
});
}
out
}