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