use std::sync::Arc;
use azul_core::{
dom::NodeId,
geom::{LogicalPosition, LogicalSize},
ui_solver::GlyphInstance,
};
use azul_css::props::basic::ColorU;
use azul_css::props::style::StyleBackgroundContent;
use crate::text3::cache::{
get_item_vertical_metrics, InlineBorderInfo, LoadedFonts, ParsedFontTrait, Point,
PositionedItem, ShapedGlyph, ShapedItem, UnifiedLayout,
};
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PositionedGlyph {
pub glyph_id: u16,
pub position: Point,
pub advance: f32,
}
#[derive(Debug, Clone)]
pub struct SimpleGlyphRun {
pub glyphs: Vec<GlyphInstance>,
pub color: ColorU,
pub background_color: Option<ColorU>,
pub background_content: Vec<StyleBackgroundContent>,
pub border: Option<InlineBorderInfo>,
pub font_hash: u64,
pub font_size_px: f32,
pub text_decoration: crate::text3::cache::TextDecoration,
pub is_ime_preview: bool,
pub source_node_id: Option<NodeId>,
}
#[derive(Debug, Clone)]
pub struct GlyphRun<T: ParsedFontTrait> {
pub glyphs: Vec<GlyphInstance>,
pub color: ColorU,
pub font: T, pub font_hash: u64,
pub font_size_px: f32,
pub text_decoration: crate::text3::cache::TextDecoration,
pub is_ime_preview: bool,
}
pub fn get_glyph_runs_simple(layout: &UnifiedLayout) -> Vec<SimpleGlyphRun> {
let mut runs: Vec<SimpleGlyphRun> = Vec::new();
let mut current_run: Option<SimpleGlyphRun> = None;
for item in &layout.items {
let (item_ascent, _) = get_item_vertical_metrics(&item.item);
let baseline_y = item.position.y + item_ascent;
let mut process_glyphs =
|positioned_glyphs: &[ShapedGlyph],
item_origin_x: f32,
writing_mode: crate::text3::cache::WritingMode,
source_node_id: Option<NodeId>| {
let mut pen_x = item_origin_x;
for glyph in positioned_glyphs {
let glyph_color = glyph.style.color;
let glyph_background = glyph.style.background_color;
let glyph_background_content = glyph.style.background_content.clone();
let glyph_border = glyph.style.border.clone();
let font_hash = glyph.font_hash;
let font_size_px = glyph.style.font_size_px;
let text_decoration = glyph.style.text_decoration.clone();
let absolute_position = LogicalPosition {
x: pen_x + glyph.offset.x,
y: baseline_y - glyph.offset.y,
};
let instance =
glyph.into_glyph_instance_at_simple(writing_mode, absolute_position);
if let Some(run) = current_run.as_mut() {
if run.font_hash == font_hash
&& run.color == glyph_color
&& run.background_color == glyph_background
&& run.background_content == glyph_background_content
&& run.border == glyph_border
&& run.font_size_px == font_size_px
&& run.text_decoration == text_decoration
&& run.source_node_id == source_node_id
{
run.glyphs.push(instance);
} else {
runs.push(run.clone());
current_run = Some(SimpleGlyphRun {
glyphs: vec![instance],
color: glyph_color,
background_color: glyph_background,
background_content: glyph_background_content.clone(),
border: glyph_border.clone(),
font_hash,
font_size_px,
text_decoration: text_decoration.clone(),
is_ime_preview: false,
source_node_id,
});
}
} else {
current_run = Some(SimpleGlyphRun {
glyphs: vec![instance],
color: glyph_color,
background_color: glyph_background,
background_content: glyph_background_content.clone(),
border: glyph_border.clone(),
font_hash,
font_size_px,
text_decoration: text_decoration.clone(),
is_ime_preview: false,
source_node_id,
});
}
pen_x += glyph.advance;
}
};
match &item.item {
ShapedItem::Cluster(cluster) => {
let writing_mode = cluster.style.writing_mode;
process_glyphs(&cluster.glyphs, item.position.x, writing_mode, cluster.source_node_id);
}
ShapedItem::CombinedBlock { glyphs, .. } => {
for g in glyphs {
let writing_mode = g.style.writing_mode;
process_glyphs(&[g.clone()], item.position.x, writing_mode, None);
}
}
_ => {}
}
}
if let Some(run) = current_run {
runs.push(run);
}
runs
}
pub fn get_glyph_runs<T: ParsedFontTrait>(
layout: &UnifiedLayout,
fonts: &LoadedFonts<T>,
) -> Vec<GlyphRun<T>> {
let mut runs: Vec<GlyphRun<T>> = Vec::new();
let mut current_run: Option<GlyphRun<T>> = None;
for item in &layout.items {
let (item_ascent, _) = get_item_vertical_metrics(&item.item);
let baseline_y = item.position.y + item_ascent;
let mut process_glyphs =
|positioned_glyphs: &[ShapedGlyph],
item_origin_x: f32,
writing_mode: crate::text3::cache::WritingMode| {
let mut pen_x = item_origin_x;
for glyph in positioned_glyphs {
let glyph_color = glyph.style.color;
let font_hash = glyph.font_hash;
let font_size_px = glyph.style.font_size_px;
let text_decoration = glyph.style.text_decoration.clone();
let font = match fonts.get_by_hash(font_hash) {
Some(f) => f.clone(),
None => continue, };
let absolute_position = LogicalPosition {
x: pen_x + glyph.offset.x,
y: baseline_y - glyph.offset.y, };
let instance =
glyph.into_glyph_instance_at(writing_mode, absolute_position, fonts);
if let Some(run) = current_run.as_mut() {
if run.font_hash == font_hash
&& run.color == glyph_color
&& run.font_size_px == font_size_px
&& run.text_decoration == text_decoration
{
run.glyphs.push(instance);
} else {
runs.push(run.clone());
current_run = Some(GlyphRun {
glyphs: vec![instance],
color: glyph_color,
font: font.clone(),
font_hash,
font_size_px,
text_decoration: text_decoration.clone(),
is_ime_preview: false, });
}
} else {
current_run = Some(GlyphRun {
glyphs: vec![instance],
color: glyph_color,
font: font.clone(),
font_hash,
font_size_px,
text_decoration: text_decoration.clone(),
is_ime_preview: false, });
}
pen_x += glyph.advance;
}
};
match &item.item {
ShapedItem::Cluster(cluster) => {
let writing_mode = cluster.style.writing_mode;
process_glyphs(&cluster.glyphs, item.position.x, writing_mode);
}
ShapedItem::CombinedBlock {
glyphs,
source,
bounds,
baseline_offset,
} => {
for g in glyphs {
let writing_mode = g.style.writing_mode;
process_glyphs(&[g.clone()], item.position.x, writing_mode);
}
}
_ => {
}
}
}
if let Some(run) = current_run {
runs.push(run);
}
runs
}
#[derive(Debug, Clone)]
pub struct PdfGlyphRun<T: ParsedFontTrait> {
pub glyphs: Vec<PdfPositionedGlyph>,
pub color: ColorU,
pub background_color: Option<ColorU>,
pub font: T,
pub font_hash: u64,
pub font_size_px: f32,
pub text_decoration: crate::text3::cache::TextDecoration,
pub line_index: usize,
pub direction: crate::text3::cache::BidiDirection,
pub writing_mode: crate::text3::cache::WritingMode,
pub baseline_start: Point,
pub cluster_texts: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct PdfPositionedGlyph {
pub glyph_id: u16,
pub position: Point,
pub advance: f32,
pub unicode_codepoint: String,
}
pub fn get_glyph_runs_pdf<T: ParsedFontTrait>(
layout: &UnifiedLayout,
fonts: &LoadedFonts<T>,
) -> Vec<PdfGlyphRun<T>> {
let mut runs: Vec<PdfGlyphRun<T>> = Vec::new();
let mut current_run: Option<PdfGlyphRun<T>> = None;
for positioned_item in &layout.items {
let cluster = match &positioned_item.item {
ShapedItem::Cluster(c) => c,
_ => continue, };
if cluster.glyphs.is_empty() {
continue;
}
let (item_ascent, _) = get_item_vertical_metrics(&positioned_item.item);
let baseline_y = positioned_item.position.y + item_ascent;
let mut pen_x = positioned_item.position.x;
let cluster_text = &cluster.text;
let cluster_glyphs_count = cluster.glyphs.len();
for (glyph_idx, glyph) in cluster.glyphs.iter().enumerate() {
let glyph_color = glyph.style.color;
let glyph_background = glyph.style.background_color;
let font_hash = glyph.font_hash;
let font_size_px = glyph.style.font_size_px;
let text_decoration = glyph.style.text_decoration.clone();
let line_index = positioned_item.line_index;
let direction = cluster.direction;
let writing_mode = cluster.style.writing_mode;
let font = match fonts.get_by_hash(font_hash) {
Some(f) => f.clone(),
None => continue, };
let glyph_position = Point {
x: pen_x + glyph.offset.x,
y: baseline_y - glyph.offset.y, };
let unicode_codepoint = if cluster_glyphs_count == 1 {
cluster_text.clone()
} else {
let byte_offset = glyph.cluster_offset as usize;
if byte_offset < cluster_text.len() {
cluster_text[byte_offset..]
.chars()
.next()
.map(|c| c.to_string())
.unwrap_or_else(|| cluster_text.clone())
} else {
if glyph_idx == 0 {
cluster_text.clone()
} else {
String::new()
}
}
};
let pdf_glyph = PdfPositionedGlyph {
glyph_id: glyph.glyph_id,
position: glyph_position,
advance: glyph.advance,
unicode_codepoint,
};
let should_break = if let Some(run) = current_run.as_ref() {
run.font_hash != font_hash
|| run.color != glyph_color
|| run.background_color != glyph_background
|| run.font_size_px != font_size_px
|| run.text_decoration != text_decoration
|| run.line_index != line_index
|| run.direction != direction
|| run.writing_mode != writing_mode
} else {
false
};
if should_break {
if let Some(run) = current_run.take() {
runs.push(run);
}
}
if let Some(run) = current_run.as_mut() {
run.glyphs.push(pdf_glyph);
run.cluster_texts.push(cluster.text.clone());
} else {
current_run = Some(PdfGlyphRun {
glyphs: vec![pdf_glyph],
color: glyph_color,
background_color: glyph_background,
font: font.clone(),
font_hash,
font_size_px,
text_decoration: text_decoration.clone(),
line_index,
direction,
writing_mode,
baseline_start: Point {
x: pen_x,
y: baseline_y,
},
cluster_texts: vec![cluster.text.clone()],
});
}
let old_pen_x = pen_x;
pen_x += glyph.advance;
}
}
if let Some(run) = current_run {
runs.push(run);
}
runs
}
pub fn get_glyph_positions(layout: &UnifiedLayout) -> Vec<PositionedGlyph> {
let mut final_glyphs = Vec::new();
for item in &layout.items {
let (item_ascent, _) = get_item_vertical_metrics(&item.item);
let baseline_y = item.position.y + item_ascent;
let mut process_glyphs = |positioned_glyphs: &[ShapedGlyph], item_origin_x: f32| {
let mut pen_x = item_origin_x;
for glyph in positioned_glyphs {
let glyph_pos = Point {
x: pen_x + glyph.offset.x,
y: baseline_y - glyph.offset.y,
};
final_glyphs.push(PositionedGlyph {
glyph_id: glyph.glyph_id,
position: glyph_pos,
advance: glyph.advance,
});
pen_x += glyph.advance;
}
};
match &item.item {
ShapedItem::Cluster(cluster) => {
process_glyphs(&cluster.glyphs, item.position.x);
}
ShapedItem::CombinedBlock { glyphs, .. } => {
process_glyphs(glyphs, item.position.x);
}
_ => {
}
}
}
final_glyphs
}