1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// SPDX-License-Identifier: MIT OR Apache-2.0
use core::fmt::Display;
use core::ops::Range;
use crate::{math, CacheKey, CacheKeyFlags, Color, GlyphDecorationData};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use core_maths::CoreFloat;
/// A laid out glyph
#[derive(Clone, Debug)]
pub struct LayoutGlyph {
/// Start index of cluster in original line
pub start: usize,
/// End index of cluster in original line
pub end: usize,
/// Font size of the glyph
pub font_size: f32,
/// Font weight of the glyph
pub font_weight: fontdb::Weight,
/// Line height of the glyph, will override buffer setting
pub line_height_opt: Option<f32>,
/// Font id of the glyph
pub font_id: fontdb::ID,
/// Font id of the glyph
pub glyph_id: u16,
/// X offset of hitbox
pub x: f32,
/// Y offset of hitbox
pub y: f32,
/// Width of hitbox
pub w: f32,
/// Unicode `BiDi` embedding level, character is left-to-right if `level` is divisible by 2
pub level: unicode_bidi::Level,
/// X offset in line
///
/// If you are dealing with physical coordinates, use [`Self::physical`] to obtain a
/// [`PhysicalGlyph`] for rendering.
///
/// This offset is useful when you are dealing with logical units and you do not care or
/// cannot guarantee pixel grid alignment. For instance, when you want to use the glyphs
/// for vectorial text, apply linear transformations to the layout, etc.
pub x_offset: f32,
/// Y offset in line
///
/// If you are dealing with physical coordinates, use [`Self::physical`] to obtain a
/// [`PhysicalGlyph`] for rendering.
///
/// This offset is useful when you are dealing with logical units and you do not care or
/// cannot guarantee pixel grid alignment. For instance, when you want to use the glyphs
/// for vectorial text, apply linear transformations to the layout, etc.
pub y_offset: f32,
/// Optional color override
pub color_opt: Option<Color>,
/// Metadata from `Attrs`
pub metadata: usize,
/// [`CacheKeyFlags`]
pub cache_key_flags: CacheKeyFlags,
}
/// A span of consecutive glyphs sharing the same text decoration.
#[derive(Clone, Debug, PartialEq)]
pub struct DecorationSpan {
/// Range of glyph indices in `LayoutLine::glyphs` covered by this span
pub glyph_range: Range<usize>,
/// The decoration config and metrics
pub data: GlyphDecorationData,
/// Fallback color from the first glyph's `color_opt`
pub color_opt: Option<Color>,
/// Font size from the first glyph (used to scale EM-unit metrics)
pub font_size: f32,
}
#[derive(Clone, Debug)]
pub struct PhysicalGlyph {
/// Cache key, see [`CacheKey`]
pub cache_key: CacheKey,
/// Integer component of X offset in line
pub x: i32,
/// Integer component of Y offset in line
pub y: i32,
}
impl LayoutGlyph {
pub fn physical(&self, offset: (f32, f32), scale: f32) -> PhysicalGlyph {
let x_offset = self.font_size * self.x_offset;
let y_offset = self.font_size * self.y_offset;
let (cache_key, x, y) = CacheKey::new(
self.font_id,
self.glyph_id,
self.font_size * scale,
(
(self.x + x_offset).mul_add(scale, offset.0),
math::truncf((self.y - y_offset).mul_add(scale, offset.1)), // Hinting in Y axis
),
self.font_weight,
self.cache_key_flags,
);
PhysicalGlyph { cache_key, x, y }
}
}
/// A line of laid out glyphs
#[derive(Clone, Debug)]
pub struct LayoutLine {
/// Width of the line
pub w: f32,
/// Maximum ascent of the glyphs in line
pub max_ascent: f32,
/// Maximum descent of the glyphs in line
pub max_descent: f32,
/// Maximum line height of any spans in line
pub line_height_opt: Option<f32>,
/// Glyphs in line
pub glyphs: Vec<LayoutGlyph>,
/// Text decoration spans covering ranges of glyphs
pub decorations: Vec<DecorationSpan>,
}
/// Wrapping mode
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Wrap {
/// No wrapping
None,
/// Wraps at a glyph level
Glyph,
/// Wraps at the word level
Word,
/// Wraps at the word level, or fallback to glyph level if a word can't fit on a line by itself
WordOrGlyph,
}
impl Display for Wrap {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::None => write!(f, "No Wrap"),
Self::Word => write!(f, "Word Wrap"),
Self::WordOrGlyph => write!(f, "Word Wrap or Character"),
Self::Glyph => write!(f, "Character"),
}
}
}
/// Align or justify
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Align {
Left,
Right,
Center,
Justified,
End,
}
impl Display for Align {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Left => write!(f, "Left"),
Self::Right => write!(f, "Right"),
Self::Center => write!(f, "Center"),
Self::Justified => write!(f, "Justified"),
Self::End => write!(f, "End"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum Ellipsize {
/// No Ellipsizing
#[default]
None,
/// Ellipsizes the start of the last visual line that fits within the `EllipsizeHeightLimit`
Start(EllipsizeHeightLimit),
/// Ellipsizes the middle of the last visual line that fits within the `EllipsizeHeightLimit`.
Middle(EllipsizeHeightLimit),
/// Ellipsizes the end of the last visual line that fits within the `EllipsizeHeightLimit`.
End(EllipsizeHeightLimit),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EllipsizeHeightLimit {
/// Number of lines to show before ellipsizing the rest. Only works if `Wrap` is NOT set to
/// `Wrap::None`. Otherwise, it will be ignored and the behavior will be the same as `Lines(1)`
Lines(usize),
/// Ellipsizes the last line that fits within the given height limit. If `Wrap` is set to
/// `Wrap::None`, the behavior will be the same as `Lines(1)`
Height(f32),
}
/// Metrics hinting strategy
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
pub enum Hinting {
/// No metrics hinting.
///
/// Glyphs will have subpixel coordinates.
///
/// This is the default.
#[default]
Disabled,
/// Metrics hinting.
///
/// Glyphs will be snapped to integral coordinates in the X-axis during layout.
/// This can improve readability for smaller text and/or low-DPI screens.
///
/// However, in order to get the right effect, you must use physical coordinates
/// during layout and avoid further scaling when rendering. Otherwise, the rounding
/// errors can accumulate and glyph distances may look erratic.
///
/// In other words, metrics hinting makes layouting dependent of the target
/// resolution.
Enabled,
}