salvation_cosmic_text/layout.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use core::fmt::Display;
4
5#[cfg(not(feature = "std"))]
6use alloc::vec::Vec;
7
8use crate::{math, CacheKey, CacheKeyFlags, Color};
9
10/// A laid out glyph
11#[derive(Clone, Debug)]
12pub struct LayoutGlyph {
13 /// Start index of cluster in original line
14 pub start: usize,
15 /// End index of cluster in original line
16 pub end: usize,
17 /// Font size of the glyph
18 pub font_size: f32,
19 /// Font id of the glyph
20 pub font_id: fontdb::ID,
21 /// Font id of the glyph
22 pub glyph_id: u16,
23 /// X offset of hitbox
24 pub x: f32,
25 /// Y offset of hitbox
26 pub y: f32,
27 /// Width of hitbox
28 pub w: f32,
29 /// Unicode BiDi embedding level, character is left-to-right if `level` is divisible by 2
30 pub level: unicode_bidi::Level,
31 /// X offset in line
32 ///
33 /// If you are dealing with physical coordinates, use [`Self::physical`] to obtain a
34 /// [`PhysicalGlyph`] for rendering.
35 ///
36 /// This offset is useful when you are dealing with logical units and you do not care or
37 /// cannot guarantee pixel grid alignment. For instance, when you want to use the glyphs
38 /// for vectorial text, apply linear transformations to the layout, etc.
39 pub x_offset: f32,
40 /// Y offset in line
41 ///
42 /// If you are dealing with physical coordinates, use [`Self::physical`] to obtain a
43 /// [`PhysicalGlyph`] for rendering.
44 ///
45 /// This offset is useful when you are dealing with logical units and you do not care or
46 /// cannot guarantee pixel grid alignment. For instance, when you want to use the glyphs
47 /// for vectorial text, apply linear transformations to the layout, etc.
48 pub y_offset: f32,
49 /// Optional color override
50 pub color_opt: Option<Color>,
51 /// Metadata from `Attrs`
52 pub metadata: usize,
53 /// [`CacheKeyFlags`]
54 pub cache_key_flags: CacheKeyFlags,
55}
56
57#[derive(Clone, Debug)]
58pub struct PhysicalGlyph {
59 /// Cache key, see [CacheKey]
60 pub cache_key: CacheKey,
61 /// Integer component of X offset in line
62 pub x: i32,
63 /// Integer component of Y offset in line
64 pub y: i32,
65}
66
67impl LayoutGlyph {
68 pub fn physical(&self, offset: (f32, f32), scale: f32) -> PhysicalGlyph {
69 let x_offset = self.font_size * self.x_offset;
70 let y_offset = self.font_size * self.y_offset;
71
72 let (cache_key, x, y) = CacheKey::new(
73 self.font_id,
74 self.glyph_id,
75 self.font_size * scale,
76 (
77 (self.x + x_offset) * scale + offset.0,
78 math::truncf((self.y - y_offset) * scale + offset.1), // Hinting in Y axis
79 ),
80 self.cache_key_flags,
81 );
82
83 PhysicalGlyph { cache_key, x, y }
84 }
85}
86
87/// A line of laid out glyphs
88#[derive(Clone, Debug)]
89pub struct LayoutLine {
90 /// Width of the line
91 pub w: f32,
92 /// Maximum ascent of the glyphs in line
93 pub max_ascent: f32,
94 /// Maximum descent of the glyphs in line
95 pub max_descent: f32,
96 /// Glyphs in line
97 pub glyphs: Vec<LayoutGlyph>,
98}
99
100/// Wrapping mode
101#[derive(Debug, Eq, PartialEq, Clone, Copy)]
102pub enum Wrap {
103 /// No wrapping
104 None,
105 /// Wraps at a glyph level
106 Glyph,
107 /// Wraps at the word level
108 Word,
109 /// Wraps at the word level, or fallback to glyph level if a word can't fit on a line by itself
110 WordOrGlyph,
111}
112
113impl Display for Wrap {
114 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115 match self {
116 Self::None => write!(f, "No Wrap"),
117 Self::Word => write!(f, "Word Wrap"),
118 Self::WordOrGlyph => write!(f, "Word Wrap or Character"),
119 Self::Glyph => write!(f, "Character"),
120 }
121 }
122}
123
124/// Align or justify
125#[derive(Debug, Eq, PartialEq, Clone, Copy)]
126pub enum Align {
127 Left,
128 Right,
129 Center,
130 Justified,
131 End,
132}
133
134impl Display for Align {
135 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136 match self {
137 Self::Left => write!(f, "Left"),
138 Self::Right => write!(f, "Right"),
139 Self::Center => write!(f, "Center"),
140 Self::Justified => write!(f, "Justified"),
141 Self::End => write!(f, "End"),
142 }
143 }
144}