pub mod span;
pub use span::{
FontSettingCache, FontSettingKey, SpanStyle, SpanStyleDecoration, UnderlineInfo,
UnderlineShape, EMPTY_FONT_SETTINGS,
};
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default, Debug)]
pub struct SpanId(pub usize);
impl SpanId {
pub fn to_usize(self) -> usize {
self.0
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Delta<T: Default> {
pub x: T,
pub top_y: T,
pub bottom_y: T,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TextDimensions {
pub width: f32,
pub height: f32,
pub scale: f32,
}
impl Default for TextDimensions {
fn default() -> Self {
Self {
width: 8.0, height: 16.0, scale: 1.0,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct CellMetrics {
pub cell_width: u32,
pub cell_height: u32,
pub cell_baseline: u32,
pub face_width: f64,
pub face_height: f64,
pub face_y: f64,
}
impl Default for CellMetrics {
fn default() -> Self {
Self {
cell_width: 8,
cell_height: 16,
cell_baseline: 4,
face_width: 8.0,
face_height: 16.0,
face_y: 0.0,
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct TextLayout {
pub line_height: f32,
pub font_size: f32,
pub original_font_size: f32,
pub dimensions: TextDimensions,
pub cell: CellMetrics,
}
impl TextLayout {
#[inline]
pub fn rescale(&mut self, scale_factor: f32) -> &mut Self {
self.dimensions.width *= scale_factor;
self.dimensions.height *= scale_factor;
self.dimensions.scale = scale_factor;
self
}
pub fn from_default_layout(default_layout: &RootStyle) -> Self {
Self {
line_height: default_layout.line_height,
font_size: default_layout.font_size,
original_font_size: default_layout.font_size,
dimensions: TextDimensions {
scale: default_layout.scale_factor,
..TextDimensions::default()
},
cell: CellMetrics::default(),
}
}
}
impl Default for TextLayout {
fn default() -> Self {
Self {
line_height: 1.0,
font_size: 0.0,
original_font_size: 0.0,
dimensions: TextDimensions::default(),
cell: CellMetrics::default(),
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct RichTextConfig {
pub position: Option<[f32; 2]>,
pub depth: f32,
}
impl Default for RichTextConfig {
fn default() -> Self {
Self {
position: None,
depth: 0.0,
}
}
}
impl RichTextConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_position(mut self, x: f32, y: f32) -> Self {
self.position = Some([x, y]);
self
}
pub fn with_depth(mut self, depth: f32) -> Self {
self.depth = depth;
self
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct RootStyle {
pub scale_factor: f32,
pub font_size: f32,
pub line_height: f32,
}
impl Default for RootStyle {
fn default() -> Self {
Self {
line_height: 1.0,
scale_factor: 1.0,
font_size: 14.,
}
}
}
impl RootStyle {
pub fn new(scale_factor: f32, font_size: f32, line_height: f32) -> RootStyle {
let line_height = if line_height <= 1.0 { 1.0 } else { line_height };
RootStyle {
scale_factor,
font_size,
line_height,
}
}
}
#[inline]
pub fn canonical_cell_metrics(
face_width: f64,
face_height: f64,
descent_phys: f64,
leading_phys: f64,
) -> CellMetrics {
let cell_width = face_width.round().max(1.0) as u32;
let cell_height = face_height.round().max(1.0) as u32;
let face_baseline = leading_phys * 0.5 + descent_phys;
let baseline_centered = face_baseline - (cell_height as f64 - face_height) * 0.5;
let cell_baseline = baseline_centered.round().max(0.0) as u32;
let face_y = cell_baseline as f64 - face_baseline;
CellMetrics {
cell_width,
cell_height,
cell_baseline,
face_width,
face_height,
face_y,
}
}
#[inline]
pub fn compute_cell_metrics(
font_library: &crate::font::FontLibrary,
font_size: f32,
line_height: f32,
scale_factor: f32,
) -> (TextDimensions, CellMetrics) {
let scale_f64 = scale_factor as f64;
let line_height_mod = line_height as f64;
let raw: Option<(f64, f64, f64, f64)> = {
#[cfg(target_os = "macos")]
{
font_library.ct_font(0).map(|handle| {
let m = crate::font::macos::font_metrics(&handle, font_size);
let cw = crate::font::macos::max_ascii_advance_px(&handle, font_size)
.or_else(|| {
crate::font::macos::advance_units_for_char(&handle, ' ')
.map(|(units, upem)| units * font_size / upem as f32)
})
.unwrap_or(font_size);
(
cw as f64,
m.ascent as f64,
m.descent as f64,
m.leading as f64,
)
})
}
#[cfg(not(target_os = "macos"))]
{
font_library.inner.try_read().and_then(|lib| {
let id = 0;
let (data, offset, _key) = lib.get_data(&id)?;
let font_ref = swash::FontRef::from_index(&data, offset as usize)?;
let m = font_ref.metrics(&[]);
let upem = m.units_per_em as f32;
let s = font_size / upem;
let glyph = font_ref.charmap().map(' ' as u32);
let advance = font_ref.glyph_metrics(&[]).advance_width(glyph);
let cw = if advance > 0.0 {
advance * s
} else {
font_size
};
Some((
cw as f64,
(m.ascent * s) as f64,
(m.descent.abs() * s) as f64,
(m.leading * s) as f64,
))
})
}
};
let (face_width, face_height, descent_phys, leading_phys) =
if let Some((cw, ascent, descent, leading)) = raw {
let face_width = cw * scale_f64;
let face_height = (ascent + descent + leading) * line_height_mod * scale_f64;
(
face_width,
face_height,
descent * line_height_mod * scale_f64,
leading * line_height_mod * scale_f64,
)
} else {
let fw = font_size as f64 * scale_f64;
let fh = font_size as f64 * line_height_mod * scale_f64;
(fw, fh, 0.0, 0.0)
};
let cell =
canonical_cell_metrics(face_width, face_height, descent_phys, leading_phys);
let dims = TextDimensions {
width: cell.cell_width as f32,
height: cell.cell_height as f32,
scale: scale_factor,
};
(dims, cell)
}