cosmic_text/
layout.rs

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