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