use crate::metadata::Metadata;
use core::mem;
use cosmic_text::LayoutGlyph;
use line_straddler::{Glyph, GlyphStyle, Line as LsLine, LineGenerator, LineType};
use piet::kurbo::{Line, Point, Rect};
use piet::{Color, FontWeight};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StyledLine {
pub line: Line,
pub color: Color,
pub bold: FontWeight,
pub font_size: f32,
}
impl StyledLine {
pub fn into_rect(self) -> Rect {
const FONT_WEIGHT_MULTIPLIER: f32 = 0.05;
const OFFSET_MULTIPLIER: f32 = -0.83;
let offset = self.font_size * OFFSET_MULTIPLIER;
let width = self.font_size
* (self.bold.to_raw() as f32 / FontWeight::NORMAL.to_raw() as f32)
* FONT_WEIGHT_MULTIPLIER;
let mut p0 = self.line.p0;
let mut p1 = self.line.p1;
p0.y += f64::from(offset);
p1.y = p0.y - f64::from(width);
Rect::from_points(p0, p1)
}
}
#[derive(Debug)]
pub struct LineProcessor {
underline: LineGenerator,
strikethrough: LineGenerator,
lines: Vec<StyledLine>,
last_glyph_size: f32,
}
impl Default for LineProcessor {
fn default() -> Self {
Self::new()
}
}
impl LineProcessor {
pub fn new() -> Self {
Self {
underline: LineGenerator::new(LineType::Underline),
strikethrough: LineGenerator::new(LineType::StrikeThrough),
lines: Vec::new(),
last_glyph_size: 0.0,
}
}
pub fn handle_glyph(&mut self, glyph: &LayoutGlyph, line_y: f32, color: cosmic_text::Color) {
let metadata = Metadata::from_raw(glyph.metadata);
let font_size = glyph.font_size;
let glyph = Glyph {
line_y,
font_size,
width: glyph.w,
x: glyph.x,
style: GlyphStyle {
boldness: metadata.boldness().to_raw(),
color: match glyph.color_opt {
Some(color) => {
let [r, g, b, a] = [color.r(), color.g(), color.b(), color.a()];
line_straddler::Color::rgba(r, g, b, a)
}
None => {
let [r, g, b, a] = [color.r(), color.g(), color.b(), color.a()];
line_straddler::Color::rgba(r, g, b, a)
}
},
},
};
let Self {
underline,
strikethrough,
lines,
last_glyph_size,
} = self;
let handle_meta = |generator: &mut LineGenerator, has_it| {
let line = if has_it {
generator.add_glyph(glyph)
} else {
generator.pop_line()
};
line.map(|line| cvt_line(line, font_size))
};
let underline = handle_meta(underline, metadata.underline());
let strikethrough = handle_meta(strikethrough, metadata.strikethrough());
lines.extend(underline);
lines.extend(strikethrough);
*last_glyph_size = font_size;
}
pub fn lines(&mut self) -> Vec<StyledLine> {
let underline = self.underline.pop_line();
let strikethrough = self.strikethrough.pop_line();
let font_size = self.last_glyph_size;
self.lines
.extend(underline.map(|line| cvt_line(line, font_size)));
self.lines
.extend(strikethrough.map(|line| cvt_line(line, font_size)));
mem::take(&mut self.lines)
}
}
fn cvt_line(ls_line: LsLine, font_size: f32) -> StyledLine {
let line = Line {
p0: Point::new(ls_line.start_x.into(), ls_line.y.into()),
p1: Point::new(ls_line.end_x.into(), ls_line.y.into()),
};
StyledLine {
line,
color: cvt_color(ls_line.style.color),
bold: FontWeight::new(ls_line.style.boldness),
font_size,
}
}
fn cvt_color(color: line_straddler::Color) -> Color {
let [r, g, b, a] = color.components();
Color::rgba8(r, g, b, a)
}