1use crate::geometry::{
2 TextLineCluster, caret_stops_for_slice, metrics_from_wrapped_lines,
3 shaped_line_visual_x_bounds_px,
4};
5use crate::line_layout::TextLineLayout;
6use crate::parley_shaper::{ParleyGlyph, ShapedCluster};
7use crate::wrapper::WrappedLayout;
8use fret_core::{TextAlign, TextConstraints, TextMetrics, geometry::Px};
9use std::ops::Range;
10use std::sync::Arc;
11
12#[derive(Debug, Clone)]
13pub struct PreparedLine {
14 layout: TextLineLayout,
15 glyphs: Vec<ParleyGlyph>,
16}
17
18#[derive(Debug, Clone)]
19pub struct PreparedLayout {
20 kept_end: usize,
21 metrics: TextMetrics,
22 lines: Vec<PreparedLine>,
23 first_line_caret_stops: Vec<(usize, Px)>,
24 missing_glyphs: u32,
25}
26
27impl PreparedLine {
28 fn new(layout: TextLineLayout, glyphs: Vec<ParleyGlyph>) -> Self {
29 Self { layout, glyphs }
30 }
31
32 pub fn layout(&self) -> &TextLineLayout {
33 &self.layout
34 }
35
36 pub fn glyphs(&self) -> &[ParleyGlyph] {
37 &self.glyphs
38 }
39
40 pub fn into_parts(self) -> (TextLineLayout, Vec<ParleyGlyph>) {
41 (self.layout, self.glyphs)
42 }
43}
44
45impl PreparedLayout {
46 fn new(
47 kept_end: usize,
48 metrics: TextMetrics,
49 lines: Vec<PreparedLine>,
50 first_line_caret_stops: Vec<(usize, Px)>,
51 missing_glyphs: u32,
52 ) -> Self {
53 Self {
54 kept_end,
55 metrics,
56 lines,
57 first_line_caret_stops,
58 missing_glyphs,
59 }
60 }
61
62 pub fn kept_end(&self) -> usize {
63 self.kept_end
64 }
65
66 pub fn metrics(&self) -> TextMetrics {
67 self.metrics
68 }
69
70 pub fn lines(&self) -> &[PreparedLine] {
71 &self.lines
72 }
73
74 pub fn first_line_caret_stops(&self) -> &[(usize, Px)] {
75 &self.first_line_caret_stops
76 }
77
78 pub fn missing_glyphs(&self) -> u32 {
79 self.missing_glyphs
80 }
81
82 pub fn into_parts(self) -> (usize, TextMetrics, Vec<PreparedLine>, Vec<(usize, Px)>, u32) {
83 (
84 self.kept_end,
85 self.metrics,
86 self.lines,
87 self.first_line_caret_stops,
88 self.missing_glyphs,
89 )
90 }
91}
92
93fn align_offset_px_for_line(
94 constraints: TextConstraints,
95 scale: f32,
96 line_min_x_px: f32,
97 line_visual_width_px: f32,
98) -> f32 {
99 let container_width_px = constraints
100 .max_width
101 .map(|w| w.0 * scale)
102 .unwrap_or_else(|| line_visual_width_px.max(0.0));
103 let slack_px = (container_width_px - line_visual_width_px.max(0.0)).max(0.0);
104 let target_left_px = match constraints.align {
105 TextAlign::Start => 0.0,
106 TextAlign::Center => slack_px * 0.5,
107 TextAlign::End => slack_px,
108 };
109 target_left_px - line_min_x_px
110}
111
112fn clusters_for_line(
113 line_clusters: &[ShapedCluster],
114 line_range: Range<usize>,
115 kept_end: usize,
116 line_align_offset_px: f32,
117 scale: f32,
118) -> Arc<[TextLineCluster]> {
119 if line_clusters.is_empty() {
120 return Arc::from([]);
121 }
122
123 let mut out: Vec<TextLineCluster> = Vec::with_capacity(line_clusters.len());
124 for c in line_clusters {
125 let text_range = c.text_range();
126 let start = (line_range.start + text_range.start).min(kept_end);
127 let end = (line_range.start + text_range.end).min(kept_end);
128 if start >= end {
129 continue;
130 }
131
132 let x0 = ((c.x0() + line_align_offset_px) / scale).max(0.0);
133 let x1 = ((c.x1() + line_align_offset_px) / scale).max(0.0);
134 let x0 = if x0.is_finite() { Px(x0) } else { Px(0.0) };
135 let x1 = if x1.is_finite() { Px(x1) } else { Px(0.0) };
136
137 out.push(TextLineCluster::new(start..end, x0, x1, c.is_rtl()));
138 }
139
140 Arc::from(out)
141}
142
143pub fn prepare_layout_from_wrapped(
144 text: &str,
145 wrapped: WrappedLayout,
146 constraints: TextConstraints,
147 scale: f32,
148 snap_vertical: bool,
149) -> PreparedLayout {
150 let (_, kept_end, line_ranges, mut wrapped_lines) = wrapped.into_parts();
151
152 let first_baseline_px = wrapped_lines
153 .first()
154 .map(|l| l.baseline().max(0.0))
155 .unwrap_or(0.0);
156 let first_baseline_px = if snap_vertical && let Some(first) = wrapped_lines.first() {
157 let top_px = 0.0_f32;
158 let bottom_px = (top_px + first.line_height().max(0.0)).round().max(top_px);
159 let height_px = (bottom_px - top_px).max(0.0);
160 (top_px + first.baseline().max(0.0))
161 .round()
162 .clamp(top_px, top_px + height_px)
163 } else {
164 first_baseline_px
165 };
166
167 let metrics = metrics_from_wrapped_lines(&wrapped_lines, scale);
168
169 let mut out_lines: Vec<PreparedLine> = Vec::with_capacity(wrapped_lines.len().max(1));
170 let mut first_line_caret_stops: Vec<(usize, Px)> = Vec::new();
171 let mut missing_glyphs: u32 = 0;
172
173 let mut line_top_px = 0.0_f32;
174
175 for (i, (range, mut line)) in line_ranges
176 .into_iter()
177 .zip(wrapped_lines.drain(..))
178 .enumerate()
179 {
180 if snap_vertical {
181 line_top_px = line_top_px.round();
182 }
183
184 let line_height_px_raw = line.line_height().max(0.0);
185 let line_baseline_px_raw = line.baseline().max(0.0);
186
187 let (line_height_px, baseline_pos_px) = if snap_vertical {
188 let bottom_px = (line_top_px + line_height_px_raw).round().max(line_top_px);
189 let height_px = (bottom_px - line_top_px).max(0.0);
190 let baseline_pos_px = (line_top_px + line_baseline_px_raw)
191 .round()
192 .clamp(line_top_px, line_top_px + height_px);
193 (height_px, baseline_pos_px)
194 } else {
195 (line_height_px_raw, line_top_px + line_baseline_px_raw)
196 };
197
198 let line_offset_px = baseline_pos_px - first_baseline_px;
199
200 let slice = &text[range.clone()];
201 let (line_min_x_px, line_max_x_px) = shaped_line_visual_x_bounds_px(&line);
202 let line_visual_width_px = (line_max_x_px - line_min_x_px).max(0.0);
203 let line_align_offset_px =
204 align_offset_px_for_line(constraints, scale, line_min_x_px, line_visual_width_px);
205 let line_align_offset = Px(line_align_offset_px / scale);
206
207 let clusters = clusters_for_line(
208 line.clusters(),
209 range.clone(),
210 kept_end,
211 line_align_offset_px,
212 scale,
213 );
214
215 let mut caret_stops = caret_stops_for_slice(
216 slice,
217 range.start,
218 line.clusters(),
219 line_visual_width_px.max(0.0),
220 scale,
221 kept_end,
222 );
223 if line_align_offset.0 != 0.0 {
224 for (_, x) in caret_stops.iter_mut() {
225 *x = Px(x.0 + line_align_offset.0);
226 }
227 }
228 if i == 0 {
229 first_line_caret_stops = caret_stops.clone();
230 }
231
232 for g in line.glyphs_mut().iter_mut() {
233 if g.id() == 0 {
234 missing_glyphs = missing_glyphs.saturating_add(1);
235 }
236 g.set_x(g.x() + line_align_offset_px);
237 g.set_y(g.y() + line_offset_px);
238 let glyph_range = g.text_range();
239 g.set_text_range((range.start + glyph_range.start)..(range.start + glyph_range.end));
240 }
241
242 let layout = TextLineLayout::new(
243 range.start,
244 range.end.min(kept_end),
245 Px((line_visual_width_px / scale).max(0.0)),
246 Px((line_top_px / scale).max(0.0)),
247 Px((baseline_pos_px / scale).max(0.0)),
248 Px(((line_height_px / scale).max(0.0)).max(1.0)),
249 Px((line.ascent().abs().max(0.0) / scale).max(0.0)),
250 Px((line.descent().abs().max(0.0) / scale).max(0.0)),
251 Px((line.ink_ascent().abs().max(0.0) / scale).max(0.0)),
252 Px((line.ink_descent().abs().max(0.0) / scale).max(0.0)),
253 caret_stops,
254 clusters,
255 );
256
257 out_lines.push(PreparedLine::new(layout, line.take_glyphs()));
258
259 line_top_px += line_height_px;
260 }
261
262 if out_lines.is_empty() {
264 let caret_stops = vec![(0, Px(0.0))];
265 first_line_caret_stops = caret_stops.clone();
266 out_lines.push(PreparedLine::new(
267 TextLineLayout::new(
268 0,
269 0,
270 Px(0.0),
271 Px(0.0),
272 Px(0.0),
273 Px(1.0),
274 Px(0.0),
275 Px(0.0),
276 Px(0.0),
277 Px(0.0),
278 caret_stops,
279 Arc::from([]),
280 ),
281 Vec::new(),
282 ));
283 }
284
285 PreparedLayout::new(
286 kept_end,
287 metrics,
288 out_lines,
289 first_line_caret_stops,
290 missing_glyphs,
291 )
292}