use crate::{
characters::{Character, Characters},
linebreak::{LineBreak, LineBreaker},
lines::Lines,
SectionGlyph, SectionText,
};
use ab_glyph::*;
use std::iter::{FusedIterator, Iterator, Peekable};
#[derive(Clone, Debug, Default)]
pub(crate) struct VMetrics {
pub ascent: f32,
pub descent: f32,
pub line_gap: f32,
}
impl VMetrics {
#[inline]
pub fn height(&self) -> f32 {
self.ascent - self.descent
}
#[inline]
pub fn max(self, other: Self) -> Self {
if other.height() > self.height() {
other
} else {
self
}
}
}
impl<F: Font> From<PxScaleFont<F>> for VMetrics {
#[inline]
fn from(scale_font: PxScaleFont<F>) -> Self {
Self {
ascent: scale_font.ascent(),
descent: scale_font.descent(),
line_gap: scale_font.line_gap(),
}
}
}
pub(crate) struct Word {
pub glyphs: Vec<SectionGlyph>,
pub layout_width: f32,
pub layout_width_no_trail: f32,
pub max_v_metrics: VMetrics,
pub hard_break: bool,
}
pub(crate) struct Words<'a, 'b, L, F, S>
where
L: LineBreaker,
F: Font,
S: Iterator<Item = SectionText<'a>>,
{
pub(crate) characters: Peekable<Characters<'a, 'b, L, F, S>>,
}
impl<'a, 'b, L, F, S> Words<'a, 'b, L, F, S>
where
L: LineBreaker,
F: Font,
S: Iterator<Item = SectionText<'a>>,
{
pub(crate) fn lines(self, width_bound: f32) -> Lines<'a, 'b, L, F, S> {
Lines {
words: self.peekable(),
width_bound,
}
}
}
impl<'a, L, F, S> Iterator for Words<'a, '_, L, F, S>
where
L: LineBreaker,
F: Font,
S: Iterator<Item = SectionText<'a>>,
{
type Item = Word;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let mut glyphs = Vec::new();
let mut caret = 0.0;
let mut caret_no_trail = caret;
let mut last_glyph_id = None;
let mut max_v_metrics = VMetrics::default();
let mut hard_break = false;
let mut progress = false;
for Character {
mut glyph,
scale_font,
font_id,
line_break,
control,
whitespace,
section_index,
byte_index,
} in &mut self.characters
{
progress = true;
max_v_metrics = max_v_metrics.max(scale_font.into());
if let Some(id) = last_glyph_id.take() {
caret += scale_font.kern(id, glyph.id);
}
last_glyph_id = Some(glyph.id);
if !control {
let advance_width = scale_font.h_advance(glyph.id);
glyph.position = point(caret, 0.0);
glyphs.push(SectionGlyph {
section_index,
byte_index,
glyph,
font_id,
});
caret += advance_width;
if !whitespace {
caret_no_trail = caret;
}
}
if line_break.is_some() {
if let Some(LineBreak::Hard(..)) = line_break {
hard_break = true;
} else if self.characters.peek().is_none() {
hard_break = true;
}
break;
}
}
if progress {
return Some(Word {
glyphs,
layout_width: caret,
layout_width_no_trail: caret_no_trail,
hard_break,
max_v_metrics,
});
}
None
}
}
impl<'a, L, F, S> FusedIterator for Words<'a, '_, L, F, S>
where
L: LineBreaker,
F: Font,
S: Iterator<Item = SectionText<'a>>,
{
}