use crate::{Color, FontId, Op, Pt, Rect, Rgb};
#[cfg(feature = "text_layout")]
use azul_layout::text3::{
cache::{LoadedFonts, ParsedFontTrait, UnifiedLayout},
glyphs::get_glyph_runs_pdf,
};
#[cfg(feature = "text_layout")]
use azul_css::props::basic::ColorU;
#[derive(Debug, Clone)]
pub struct TextHole {
pub rect: Rect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextAlign {
#[default]
Left,
Center,
Right,
Justify,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextDirection {
#[default]
Ltr,
Rtl,
}
#[derive(Debug, Clone)]
pub struct TextShapingOptions {
pub font_size: Pt,
pub line_height: Option<Pt>,
pub letter_spacing: Option<f32>,
pub word_spacing: Option<f32>,
pub tab_width: Option<f32>,
pub max_width: Option<Pt>,
pub max_height: Option<Pt>,
pub align: TextAlign,
pub direction: TextDirection,
pub color: Color,
pub holes: Vec<TextHole>,
}
impl TextShapingOptions {
pub fn new(font_size: Pt) -> Self {
Self {
font_size,
..Default::default()
}
}
pub fn with_max_width(mut self, max_width: Pt) -> Self {
self.max_width = Some(max_width);
self
}
pub fn with_max_height(mut self, max_height: Pt) -> Self {
self.max_height = Some(max_height);
self
}
pub fn with_align(mut self, align: TextAlign) -> Self {
self.align = align;
self
}
pub fn with_direction(mut self, direction: TextDirection) -> Self {
self.direction = direction;
self
}
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
}
pub fn with_line_height(mut self, line_height: Pt) -> Self {
self.line_height = Some(line_height);
self
}
pub fn with_letter_spacing(mut self, letter_spacing: f32) -> Self {
self.letter_spacing = Some(letter_spacing);
self
}
}
impl Default for TextShapingOptions {
fn default() -> Self {
Self {
font_size: Pt(12.0),
line_height: None,
letter_spacing: None,
word_spacing: None,
tab_width: None,
max_width: None,
max_height: None,
align: TextAlign::default(),
direction: TextDirection::default(),
color: Color::Rgb(Rgb {
r: 0.0,
g: 0.0,
b: 0.0,
icc_profile: None,
}),
holes: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ShapedWord {
pub text: String,
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
pub index: usize,
}
#[derive(Debug, Clone)]
pub struct ShapedLine {
pub words: Vec<ShapedWord>,
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
pub index: usize,
}
#[derive(Debug, Clone)]
pub struct ShapedText {
pub font_id: FontId,
pub options: TextShapingOptions,
pub lines: Vec<ShapedLine>,
pub width: f32,
pub height: f32,
}
impl ShapedText {
pub fn bounds(&self) -> Rect {
Rect {
x: Pt(0.0),
y: Pt(0.0),
width: Pt(self.width),
height: Pt(self.height),
mode: None,
winding_order: None,
}
}
pub fn is_empty(&self) -> bool {
self.lines.is_empty()
}
pub fn line_count(&self) -> usize {
self.lines.len()
}
}
#[cfg(feature = "text_layout")]
pub fn layout_to_ops<T: ParsedFontTrait + 'static>(
layout: &UnifiedLayout,
page_height: Pt,
font_id: &FontId,
loaded_fonts: &LoadedFonts<T>,
default_color: Color,
) -> Vec<Op> {
layout_to_ops_with_offset(layout, page_height, font_id, loaded_fonts, default_color, Pt(0.0), Pt(0.0))
}
#[cfg(feature = "text_layout")]
pub fn layout_to_ops_with_offset<T: ParsedFontTrait + 'static>(
layout: &UnifiedLayout,
page_height: Pt,
_font_id: &FontId,
loaded_fonts: &LoadedFonts<T>,
_default_color: Color,
offset_x: Pt,
offset_y: Pt,
) -> Vec<Op> {
let mut ops = Vec::new();
let glyph_runs = get_glyph_runs_pdf(layout, loaded_fonts);
if glyph_runs.is_empty() {
return ops;
}
let mut current_color: Option<ColorU> = None;
for run in glyph_runs.iter() {
if run.glyphs.is_empty() {
continue;
}
if current_color != Some(run.color) {
ops.push(Op::SetFillColor {
col: coloru_to_color(&run.color),
});
current_color = Some(run.color);
}
let run_font_id = FontId(format!("F{}", run.font_hash));
ops.push(Op::SetFont {
font: crate::ops::PdfFontHandle::External(run_font_id),
size: Pt(run.font_size_px),
});
ops.push(Op::StartTextSection);
for glyph in &run.glyphs {
let pdf_x = glyph.position.x + offset_x.0;
let pdf_y = page_height.0 - glyph.position.y - offset_y.0;
ops.push(Op::SetTextMatrix {
matrix: crate::matrix::TextMatrix::Raw([
1.0, 0.0, 0.0, 1.0, pdf_x, pdf_y, ]),
});
ops.push(Op::ShowText {
items: vec![crate::text::TextItem::GlyphIds(vec![
crate::text::Codepoint {
gid: glyph.glyph_id,
offset: 0.0,
cid: Some(glyph.unicode_codepoint.clone()),
}
])],
});
}
ops.push(Op::EndTextSection);
}
ops
}
#[cfg(feature = "text_layout")]
fn coloru_to_color(color: &ColorU) -> Color {
Color::Rgb(Rgb {
r: color.r as f32 / 255.0,
g: color.g as f32 / 255.0,
b: color.b as f32 / 255.0,
icc_profile: None,
})
}