use crate::conv::{to_u32, to_usize, DPU};
use crate::display::RunSpecial;
use crate::fonts::{fonts, FontId};
use crate::{Range, Vec2};
use smallvec::SmallVec;
use unicode_bidi::Level;
#[repr(transparent)]
#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
pub struct GlyphId(pub u16);
#[derive(Clone, Copy, Debug)]
pub struct Glyph {
pub index: u32,
pub id: GlyphId,
pub position: Vec2,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct GlyphBreak {
pub index: u32,
pub pos: u32,
pub no_space_end: f32,
}
impl GlyphBreak {
pub(crate) fn new(index: u32) -> Self {
GlyphBreak {
index,
pos: u32::MAX,
no_space_end: f32::NAN,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct GlyphRun {
pub range: Range,
pub dpem: f32,
pub font_id: FontId,
pub special: RunSpecial,
pub level: Level,
pub glyphs: Vec<Glyph>,
pub breaks: SmallVec<[GlyphBreak; 5]>,
pub dpu: DPU,
pub height: f32,
pub no_space_end: f32,
pub caret: f32,
}
impl GlyphRun {
pub fn num_parts(&self) -> usize {
self.breaks.len() + 1
}
pub fn part_lengths(&self, range: std::ops::Range<usize>) -> (f32, f32, f32) {
assert!(range.start <= range.end);
let (mut offset, mut len_no_space, mut len) = (0.0, 0.0, 0.0);
if self.level.is_ltr() {
if range.end > 0 {
len_no_space = self.no_space_end;
len = self.caret;
if range.end <= self.breaks.len() {
let b = self.breaks[range.end - 1];
len_no_space = b.no_space_end;
if to_usize(b.pos) < self.glyphs.len() {
len = self.glyphs[to_usize(b.pos)].position.0
}
}
}
if range.start > 0 {
let glyph = to_usize(self.breaks[range.start - 1].pos);
offset = self.glyphs[glyph].position.0;
len_no_space -= offset;
len -= offset;
}
} else {
if range.start <= self.breaks.len() {
len = self.caret;
if range.start > 0 {
let b = self.breaks.len() - range.start;
let pos = to_usize(self.breaks[b].pos);
if pos < self.glyphs.len() {
len = self.glyphs[pos].position.0;
}
}
len_no_space = len;
}
if range.end <= self.breaks.len() {
offset = self.caret;
if range.end == 0 {
len_no_space = 0.0;
} else {
let b = self.breaks.len() - range.end;
let b = self.breaks[b];
len_no_space -= b.no_space_end;
if to_usize(b.pos) < self.glyphs.len() {
offset = self.glyphs[to_usize(b.pos)].position.0;
}
}
len -= offset;
}
}
(offset, len_no_space, len)
}
pub fn to_glyph_range(&self, range: std::ops::Range<usize>) -> Range {
let mut start = range.start;
let mut end = range.end;
let rtl = self.level.is_rtl();
if rtl {
let num_parts = self.num_parts();
start = num_parts - start;
end = num_parts - end;
}
let map = |part: usize| {
if part == 0 {
0
} else if part <= self.breaks.len() {
to_usize(self.breaks[part - 1].pos)
} else {
debug_assert_eq!(part, self.breaks.len() + 1);
self.glyphs.len()
}
};
let mut start = map(start);
let mut end = map(end);
if rtl {
std::mem::swap(&mut start, &mut end);
}
Range::from(start..end)
}
}
pub(crate) fn shape(
text: &str,
range: Range,
dpem: f32,
font_id: FontId,
mut breaks: SmallVec<[GlyphBreak; 5]>,
special: RunSpecial,
level: Level,
) -> GlyphRun {
if level.is_rtl() {
breaks.reverse();
}
let mut glyphs = vec![];
let mut no_space_end = 0.0;
let mut caret = 0.0;
let face = fonts().get(font_id);
let dpu = face.dpu(dpem);
let sf = face.scale_by_dpu(dpu);
if dpem >= 0.0 {
#[cfg(feature = "harfbuzz_rs")]
let r = shape_harfbuzz(text, range, dpem, font_id, level, &mut breaks);
#[cfg(not(feature = "harfbuzz_rs"))]
let r = shape_simple(sf, text, range, level, &mut breaks);
glyphs = r.0;
no_space_end = r.1;
caret = r.2;
}
if level.is_rtl() {
let mut break_i = breaks.len().wrapping_sub(1);
let mut start_no_space = caret;
let mut last_id = None;
let side_bearing = |id: Option<GlyphId>| id.map(|id| sf.h_side_bearing(id)).unwrap_or(0.0);
for (pos, glyph) in glyphs.iter().enumerate().rev() {
if break_i < breaks.len() && to_usize(breaks[break_i].pos) == pos {
assert!(pos < glyphs.len());
breaks[break_i].pos = to_u32(pos) + 1;
breaks[break_i].no_space_end = start_no_space - side_bearing(last_id);
break_i = break_i.wrapping_sub(1);
}
if !text[to_usize(glyph.index)..]
.chars()
.next()
.map(|c| c.is_whitespace())
.unwrap_or(true)
{
last_id = Some(glyph.id);
start_no_space = glyph.position.0;
}
}
no_space_end = start_no_space - side_bearing(last_id);
}
GlyphRun {
range,
dpem,
font_id,
special,
level,
glyphs,
breaks,
dpu,
height: sf.height(),
no_space_end,
caret,
}
}
#[cfg(feature = "harfbuzz_rs")]
fn shape_harfbuzz(
text: &str,
range: Range,
dpem: f32,
font_id: FontId,
level: Level,
breaks: &mut [GlyphBreak],
) -> (Vec<Glyph>, f32, f32) {
let dpem = dpem;
let mut font = fonts().get_harfbuzz(font_id);
font.set_ppem(dpem as u32, dpem as u32);
let upem = font.face().upem();
let unit_factor = dpem / (upem as f32);
let slice = &text[range];
let idx_offset = range.start;
let rtl = level.is_rtl();
let buffer = harfbuzz_rs::UnicodeBuffer::new()
.set_direction(match rtl {
false => harfbuzz_rs::Direction::Ltr,
true => harfbuzz_rs::Direction::Rtl,
})
.add_str(slice);
let features = [];
let output = harfbuzz_rs::shape(&font, buffer, &features);
let unit = |x: harfbuzz_rs::Position| x as f32 * unit_factor;
let mut caret = 0.0;
let mut no_space_end = caret;
let mut break_i = 0;
let mut glyphs = Vec::with_capacity(output.len());
for (info, pos) in output
.get_glyph_infos()
.iter()
.zip(output.get_glyph_positions().iter())
{
let index = idx_offset + info.cluster;
assert!(info.codepoint <= u16::MAX as u32, "failed to map glyph id");
let id = GlyphId(info.codepoint as u16);
if breaks
.get(break_i)
.map(|b| b.index == index)
.unwrap_or(false)
{
breaks[break_i].pos = to_u32(glyphs.len());
breaks[break_i].no_space_end = no_space_end;
break_i += 1;
}
let position = Vec2(caret + unit(pos.x_offset), unit(pos.y_offset));
glyphs.push(Glyph {
index,
id,
position,
});
debug_assert_eq!(pos.y_advance, 0);
caret += unit(pos.x_advance);
if text[to_usize(index)..]
.chars()
.next()
.map(|c| !c.is_whitespace())
.unwrap()
{
no_space_end = caret;
}
}
(glyphs, no_space_end, caret)
}
#[cfg(not(feature = "harfbuzz_rs"))]
fn shape_simple(
sf: crate::fonts::ScaledFaceRef,
text: &str,
range: Range,
level: Level,
breaks: &mut [GlyphBreak],
) -> (Vec<Glyph>, f32, f32) {
use unicode_bidi_mirroring::get_mirrored;
let slice = &text[range];
let idx_offset = range.start;
let rtl = level.is_rtl();
let mut caret = 0.0;
let mut no_space_end = caret;
let mut prev_glyph_id: Option<GlyphId> = None;
let mut break_i = 0;
let mut glyphs = Vec::with_capacity(slice.len());
let mut iter = slice.char_indices();
let mut next_char_index = || match rtl {
false => iter.next(),
true => iter.next_back(),
};
while let Some((index, mut c)) = next_char_index() {
let index = idx_offset + to_u32(index);
if rtl {
if let Some(m) = get_mirrored(c) {
c = m;
}
}
let id = sf.glyph_id(c);
if breaks
.get(break_i)
.map(|b| b.index == index)
.unwrap_or(false)
{
breaks[break_i].pos = to_u32(glyphs.len());
breaks[break_i].no_space_end = no_space_end;
break_i += 1;
no_space_end = caret;
}
if let Some(prev) = prev_glyph_id {
if let Some(adv) = sf
.face()
.kerning_subtables()
.filter(|st| st.is_horizontal() && !st.is_variable())
.find_map(|st| st.glyphs_kerning(prev.into(), id.into()))
{
caret += sf.dpu().i16_to_px(adv);
}
}
prev_glyph_id = Some(id);
let position = Vec2(caret, 0.0);
let glyph = Glyph {
index,
id,
position,
};
glyphs.push(glyph);
caret += sf.h_advance(id);
if !c.is_whitespace() {
no_space_end = caret;
}
}
glyphs.shrink_to_fit();
(glyphs, no_space_end, caret)
}