use crate::FontData;
use crate::layout::cluster::{Cluster, ClusterPath};
use crate::layout::data::{LineItemData, RunData};
use crate::layout::layout::Layout;
use crate::style::Brush;
use core::ops::Range;
use fontique::Synthesis;
#[derive(Copy, Clone)]
pub struct Run<'a, B: Brush> {
pub(crate) layout: &'a Layout<B>,
pub(crate) line_index: u32,
pub(crate) index: u32,
pub(crate) data: &'a RunData,
pub(crate) line_data: Option<&'a LineItemData>,
}
impl<'a, B: Brush> Run<'a, B> {
pub(crate) fn new(
layout: &'a Layout<B>,
line_index: u32,
index: u32,
data: &'a RunData,
line_data: Option<&'a LineItemData>,
) -> Self {
Self {
layout,
line_index,
index,
data,
line_data,
}
}
pub fn index(&self) -> usize {
self.index as usize
}
pub fn font(&self) -> &FontData {
self.layout.data.fonts.get(self.data.font_index).unwrap()
}
pub fn font_size(&self) -> f32 {
self.data.font_size
}
pub fn font_attrs(&self) -> &fontique::Attributes {
&self.data.font_attrs
}
pub fn synthesis(&self) -> Synthesis {
self.data.synthesis
}
pub fn normalized_coords(&self) -> &[i16] {
self.layout
.data
.coords
.get(self.data.coords_range.clone())
.unwrap_or(&[])
}
pub fn metrics(&self) -> &RunMetrics {
&self.data.metrics
}
pub fn advance(&self) -> f32 {
self.line_data
.map(|d| d.advance)
.unwrap_or(self.data.advance)
}
pub fn text_range(&self) -> Range<usize> {
self.line_data
.map(|d| &d.text_range)
.unwrap_or(&self.data.text_range)
.clone()
}
pub fn is_rtl(&self) -> bool {
self.data.bidi_level & 1 != 0
}
pub fn cluster_range(&self) -> Range<usize> {
self.line_data
.map(|d| &d.cluster_range)
.unwrap_or(&self.data.cluster_range)
.clone()
}
pub fn len(&self) -> usize {
self.cluster_range().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: usize) -> Option<Cluster<'a, B>> {
let range = self
.line_data
.map(|d| &d.cluster_range)
.unwrap_or(&self.data.cluster_range);
let original_index = index;
let index = range.start + index;
Some(Cluster {
path: ClusterPath::new(self.line_index, self.index, original_index as u32),
run: self.clone(),
data: self.layout.data.clusters.get(index)?,
})
}
pub fn clusters(&'a self) -> impl Iterator<Item = Cluster<'a, B>> + 'a + Clone {
let range = self.cluster_range();
Clusters {
run: self,
range,
rev: false,
}
}
pub fn logical_to_visual(&self, logical_index: usize) -> Option<usize> {
let num_clusters = self.len();
if logical_index >= num_clusters {
return None;
}
let visual_index = if self.is_rtl() {
num_clusters - 1 - logical_index
} else {
logical_index
};
Some(visual_index)
}
pub fn visual_to_logical(&self, visual_index: usize) -> Option<usize> {
let num_clusters = self.len();
if visual_index >= num_clusters {
return None;
}
let logical_index = if self.is_rtl() {
num_clusters - 1 - visual_index
} else {
visual_index
};
Some(logical_index)
}
pub fn visual_clusters(&'a self) -> impl Iterator<Item = Cluster<'a, B>> + 'a + Clone {
let range = self.cluster_range();
Clusters {
run: self,
range,
rev: self.is_rtl(),
}
}
}
struct Clusters<'a, B: Brush> {
run: &'a Run<'a, B>,
range: Range<usize>,
rev: bool,
}
impl<B: Brush> Clone for Clusters<'_, B> {
fn clone(&self) -> Self {
Self {
run: self.run,
range: self.range.clone(),
rev: self.rev,
}
}
}
impl<'a, B: Brush> Iterator for Clusters<'a, B> {
type Item = Cluster<'a, B>;
fn next(&mut self) -> Option<Self::Item> {
let index = if self.rev {
self.range.next_back()?
} else {
self.range.next()?
};
Some(Cluster {
path: ClusterPath::new(
self.run.line_index,
self.run.index,
(index - self.run.cluster_range().start) as u32,
),
run: self.run.clone(),
data: self.run.layout.data.clusters.get(index)?,
})
}
}
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct RunMetrics {
pub ascent: f32,
pub descent: f32,
pub leading: f32,
pub underline_offset: f32,
pub underline_size: f32,
pub strikethrough_offset: f32,
pub strikethrough_size: f32,
pub line_height: f32,
pub x_height: Option<f32>,
pub cap_height: Option<f32>,
}