mod font;
mod geometry;
mod outliner;
pub use font::Font;
pub use geometry::{point, vector, Point, Rect, Vector};
pub use ttf_parser::OutlineBuilder;
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct GlyphId(pub u16);
impl From<ttf_parser::GlyphId> for GlyphId {
fn from(id: ttf_parser::GlyphId) -> Self {
Self(id.0)
}
}
impl From<GlyphId> for ttf_parser::GlyphId {
fn from(id: GlyphId) -> Self {
Self(id.0)
}
}
#[derive(Clone, Debug)]
pub struct Glyph<'font> {
font: Font<'font>,
id: GlyphId,
}
impl<'font> Glyph<'font> {
pub fn font(&self) -> &Font<'font> {
&self.font
}
pub fn id(&self) -> GlyphId {
self.id
}
pub fn scaled(self, scale: Scale) -> ScaledGlyph<'font> {
let scale_y = self.font.scale_for_pixel_height(scale.y);
let scale_x = scale_y * scale.x / scale.y;
ScaledGlyph {
g: self,
scale: vector(scale_x, scale_y),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct HMetrics {
pub advance_width: f32,
pub left_side_bearing: f32,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct VMetrics {
pub ascent: f32,
pub descent: f32,
pub line_gap: f32,
}
impl core::ops::Mul<f32> for VMetrics {
type Output = VMetrics;
fn mul(self, rhs: f32) -> Self {
Self {
ascent: self.ascent * rhs,
descent: self.descent * rhs,
line_gap: self.line_gap * rhs,
}
}
}
#[derive(Clone, Debug)]
pub struct ScaledGlyph<'font> {
g: Glyph<'font>,
scale: Vector<f32>,
}
impl<'font> ScaledGlyph<'font> {
pub fn id(&self) -> GlyphId {
self.g.id()
}
#[inline]
pub fn font(&self) -> &Font<'font> {
self.g.font()
}
pub fn build_outline(&self, builder: &mut impl OutlineBuilder) -> bool {
let mut outliner =
outliner::OutlineScaler::new(builder, vector(self.scale.x, -self.scale.y));
self.font()
.inner()
.outline_glyph(self.id().into(), &mut outliner)
.is_some()
}
pub fn positioned(self, p: Point<f32>) -> PositionedGlyph<'font> {
let bb = self.pixel_bounds_at(p);
PositionedGlyph {
sg: self,
position: p,
bb,
}
}
pub fn h_metrics(&self) -> HMetrics {
let inner = self.font().inner();
let id = self.id().into();
let advance = inner.glyph_hor_advance(id).unwrap();
let left_side_bearing = inner.glyph_hor_side_bearing(id).unwrap();
HMetrics {
advance_width: advance as f32 * self.scale.x,
left_side_bearing: left_side_bearing as f32 * self.scale.x,
}
}
fn glyph_bitmap_box_subpixel(
&self,
font: &Font<'font>,
shift_x: f32,
shift_y: f32,
) -> Option<Rect<i32>> {
let ttf_parser::Rect {
x_min,
y_min,
x_max,
y_max,
} = font.inner().glyph_bounding_box(self.id().into())?;
Some(Rect {
min: point(
(x_min as f32 * self.scale.x + shift_x).floor() as i32,
(-y_max as f32 * self.scale.y + shift_y).floor() as i32,
),
max: point(
(x_max as f32 * self.scale.x + shift_x).ceil() as i32,
(-y_min as f32 * self.scale.y + shift_y).ceil() as i32,
),
})
}
#[inline]
fn pixel_bounds_at(&self, p: Point<f32>) -> Option<Rect<i32>> {
let (x_trunc, x_fract) = (p.x.trunc() as i32, p.x.fract());
let (y_trunc, y_fract) = (p.y.trunc() as i32, p.y.fract());
let Rect { min, max } = self.glyph_bitmap_box_subpixel(self.font(), x_fract, y_fract)?;
Some(Rect {
min: point(x_trunc + min.x, y_trunc + min.y),
max: point(x_trunc + max.x, y_trunc + max.y),
})
}
}
#[derive(Clone, Debug)]
pub struct PositionedGlyph<'font> {
sg: ScaledGlyph<'font>,
position: Point<f32>,
bb: Option<Rect<i32>>,
}
impl<'font> PositionedGlyph<'font> {
pub fn id(&self) -> GlyphId {
self.sg.id()
}
pub fn unpositioned(&self) -> &ScaledGlyph<'font> {
&self.sg
}
pub fn pixel_bounding_box(&self) -> Option<Rect<i32>> {
self.bb
}
pub fn position(&self) -> Point<f32> {
self.position
}
pub fn build_outline(&self, builder: &mut impl OutlineBuilder) -> bool {
let bb = if let Some(bb) = self.bb.as_ref() {
bb
} else {
return false;
};
let offset = vector(bb.min.x as f32, bb.min.y as f32);
let mut outliner = outliner::OutlineTranslator::new(builder, self.position - offset);
self.sg.build_outline(&mut outliner)
}
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Scale {
pub x: f32,
pub y: f32,
}
impl Scale {
#[inline]
pub fn uniform(s: f32) -> Scale {
Scale { x: s, y: s }
}
}
pub trait IntoGlyphId {
fn into_glyph_id(self, font: &Font<'_>) -> GlyphId;
}
impl IntoGlyphId for char {
#[inline]
fn into_glyph_id(self, font: &Font<'_>) -> GlyphId {
font.inner()
.glyph_index(self)
.unwrap_or(ttf_parser::GlyphId(0))
.into()
}
}
impl<G: Into<GlyphId>> IntoGlyphId for G {
#[inline]
fn into_glyph_id(self, _font: &Font<'_>) -> GlyphId {
self.into()
}
}
#[derive(Clone)]
pub struct LayoutIter<'a, 'font, 's> {
font: &'a Font<'font>,
chars: core::str::Chars<'s>,
caret: f32,
scale: Scale,
start: Point<f32>,
last_glyph: Option<GlyphId>,
}
impl<'a, 'font, 's> Iterator for LayoutIter<'a, 'font, 's> {
type Item = PositionedGlyph<'font>;
fn next(&mut self) -> Option<PositionedGlyph<'font>> {
self.chars.next().map(|c| {
let g = self.font.glyph(c).scaled(self.scale);
if let Some(last) = self.last_glyph {
self.caret += self.font.pair_kerning(self.scale, last, g.id());
}
let g = g.positioned(point(self.start.x + self.caret, self.start.y));
self.caret += g.sg.h_metrics().advance_width;
self.last_glyph = Some(g.id());
g
})
}
}