1use crate::{
2 color::ColorU,
3 fonts::{FontCache, FontId, GlyphId},
4 geometry::rect::RectF,
5 scene::Scene,
6};
7use core_foundation::{
8 attributed_string::CFMutableAttributedString,
9 base::{CFRange, TCFType},
10 string::CFString,
11};
12use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName};
13use ordered_float::OrderedFloat;
14use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
15use pathfinder_geometry::vector::{vec2f, Vector2F};
16use smallvec::SmallVec;
17use std::{
18 borrow::Borrow,
19 char,
20 collections::HashMap,
21 convert::TryFrom,
22 hash::{Hash, Hasher},
23 ops::Range,
24 sync::Arc,
25};
26
27pub struct TextLayoutCache {
28 prev_frame: Mutex<HashMap<CacheKeyValue, Arc<Line>>>,
29 curr_frame: RwLock<HashMap<CacheKeyValue, Arc<Line>>>,
30}
31
32impl TextLayoutCache {
33 pub fn new() -> Self {
34 Self {
35 prev_frame: Mutex::new(HashMap::new()),
36 curr_frame: RwLock::new(HashMap::new()),
37 }
38 }
39
40 pub fn finish_frame(&self) {
41 let mut prev_frame = self.prev_frame.lock();
42 let mut curr_frame = self.curr_frame.write();
43 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
44 curr_frame.clear();
45 }
46
47 pub fn layout_str<'a>(
48 &'a self,
49 text: &'a str,
50 font_size: f32,
51 runs: &'a [(Range<usize>, FontId)],
52 font_cache: &'a FontCache,
53 ) -> Arc<Line> {
54 let key = &CacheKeyRef {
55 text,
56 font_size: OrderedFloat(font_size),
57 runs,
58 } as &dyn CacheKey;
59 let curr_frame = self.curr_frame.upgradable_read();
60 if let Some(line) = curr_frame.get(key) {
61 return line.clone();
62 }
63
64 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
65 if let Some((key, line)) = self.prev_frame.lock().remove_entry(key) {
66 curr_frame.insert(key, line.clone());
67 line.clone()
68 } else {
69 let line = Arc::new(layout_str(text, font_size, runs, font_cache));
70 let key = CacheKeyValue {
71 text: text.into(),
72 font_size: OrderedFloat(font_size),
73 runs: SmallVec::from(runs),
74 };
75 curr_frame.insert(key, line.clone());
76 line
77 }
78 }
79}
80
81trait CacheKey {
82 fn key<'a>(&'a self) -> CacheKeyRef<'a>;
83}
84
85impl<'a> PartialEq for (dyn CacheKey + 'a) {
86 fn eq(&self, other: &dyn CacheKey) -> bool {
87 self.key() == other.key()
88 }
89}
90
91impl<'a> Eq for (dyn CacheKey + 'a) {}
92
93impl<'a> Hash for (dyn CacheKey + 'a) {
94 fn hash<H: Hasher>(&self, state: &mut H) {
95 self.key().hash(state)
96 }
97}
98
99#[derive(Eq, PartialEq)]
100struct CacheKeyValue {
101 text: String,
102 font_size: OrderedFloat<f32>,
103 runs: SmallVec<[(Range<usize>, FontId); 1]>,
104}
105
106impl CacheKey for CacheKeyValue {
107 fn key<'a>(&'a self) -> CacheKeyRef<'a> {
108 CacheKeyRef {
109 text: &self.text.as_str(),
110 font_size: self.font_size,
111 runs: self.runs.as_slice(),
112 }
113 }
114}
115
116impl Hash for CacheKeyValue {
117 fn hash<H: Hasher>(&self, state: &mut H) {
118 self.key().hash(state);
119 }
120}
121
122impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
123 fn borrow(&self) -> &(dyn CacheKey + 'a) {
124 self as &dyn CacheKey
125 }
126}
127
128#[derive(Copy, Clone, PartialEq, Eq, Hash)]
129struct CacheKeyRef<'a> {
130 text: &'a str,
131 font_size: OrderedFloat<f32>,
132 runs: &'a [(Range<usize>, FontId)],
133}
134
135impl<'a> CacheKey for CacheKeyRef<'a> {
136 fn key<'b>(&'b self) -> CacheKeyRef<'b> {
137 *self
138 }
139}
140
141#[derive(Default)]
142pub struct Line {
143 pub width: f32,
144 pub runs: Vec<Run>,
145 pub len: usize,
146 font_size: f32,
147}
148
149#[derive(Debug)]
150pub struct Run {
151 pub font_id: FontId,
152 pub glyphs: Vec<Glyph>,
153}
154
155#[derive(Debug)]
156pub struct Glyph {
157 pub id: GlyphId,
158 pub position: Vector2F,
159 pub index: usize,
160}
161
162impl Line {
163 pub fn x_for_index(&self, index: usize) -> f32 {
164 for run in &self.runs {
165 for glyph in &run.glyphs {
166 if glyph.index == index {
167 return glyph.position.x();
168 }
169 }
170 }
171 self.width
172 }
173
174 pub fn index_for_x(&self, x: f32) -> Option<usize> {
175 if x >= self.width {
176 None
177 } else {
178 for run in self.runs.iter().rev() {
179 for glyph in run.glyphs.iter().rev() {
180 if glyph.position.x() <= x {
181 return Some(glyph.index);
182 }
183 }
184 }
185 Some(0)
186 }
187 }
188
189 pub fn paint(
190 &self,
191 _origin: Vector2F,
192 _viewport_rect: RectF,
193 _colors: &[(Range<usize>, ColorU)],
194 _scene: Scene,
195 _font_cache: &FontCache,
196 ) {
197 // canvas.set_font_size(self.font_size);
198 // let mut colors = colors.iter().peekable();
199
200 // for run in &self.runs {
201 // let bounding_box = font_cache.bounding_box(run.font_id, self.font_size);
202 // let ascent = font_cache.scale_metric(
203 // font_cache.metric(run.font_id, |m| m.ascent),
204 // run.font_id,
205 // self.font_size,
206 // );
207 // let descent = font_cache.scale_metric(
208 // font_cache.metric(run.font_id, |m| m.descent),
209 // run.font_id,
210 // self.font_size,
211 // );
212
213 // let max_glyph_width = bounding_box.x();
214 // let font = font_cache.font(run.font_id);
215 // let font_name = font_cache.font_name(run.font_id);
216 // let is_emoji = font_cache.is_emoji(run.font_id);
217 // for glyph in &run.glyphs {
218 // let glyph_origin = origin + glyph.position - vec2f(0.0, descent);
219
220 // if glyph_origin.x() + max_glyph_width < viewport_rect.origin().x() {
221 // continue;
222 // }
223
224 // if glyph_origin.x() > viewport_rect.upper_right().x() {
225 // break;
226 // }
227
228 // while let Some((range, color)) = colors.peek() {
229 // if glyph.index >= range.end {
230 // colors.next();
231 // } else {
232 // if glyph.index == range.start {
233 // canvas.set_fill_style(FillStyle::Color(*color));
234 // }
235 // break;
236 // }
237 // }
238
239 // if is_emoji {
240 // match font_cache.render_emoji(glyph.id, self.font_size) {
241 // Ok(image) => {
242 // canvas.draw_image(image, RectF::new(glyph_origin, bounding_box));
243 // }
244 // Err(error) => log::error!("rasterizing emoji: {}", error),
245 // }
246 // } else {
247 // canvas.fill_glyph(
248 // &font,
249 // &font_name,
250 // glyph.id,
251 // glyph_origin + vec2f(0.0, ascent),
252 // );
253 // }
254 // }
255 // }
256 }
257}
258
259pub fn layout_str(
260 text: &str,
261 font_size: f32,
262 runs: &[(Range<usize>, FontId)],
263 font_cache: &FontCache,
264) -> Line {
265 let mut string = CFMutableAttributedString::new();
266 string.replace_str(&CFString::new(text), CFRange::init(0, 0));
267
268 let mut utf16_lens = text.chars().map(|c| c.len_utf16());
269 let mut prev_char_ix = 0;
270 let mut prev_utf16_ix = 0;
271
272 for (range, font_id) in runs {
273 let utf16_start = prev_utf16_ix
274 + utf16_lens
275 .by_ref()
276 .take(range.start - prev_char_ix)
277 .sum::<usize>();
278 let utf16_end = utf16_start
279 + utf16_lens
280 .by_ref()
281 .take(range.end - range.start)
282 .sum::<usize>();
283 prev_char_ix = range.end;
284 prev_utf16_ix = utf16_end;
285
286 let cf_range = CFRange::init(utf16_start as isize, (utf16_end - utf16_start) as isize);
287 let native_font = font_cache.native_font(*font_id, font_size);
288 unsafe {
289 string.set_attribute(cf_range, kCTFontAttributeName, &native_font);
290 }
291 }
292
293 let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef());
294
295 let width = line.get_typographic_bounds().width as f32;
296
297 let mut utf16_chars = text.encode_utf16();
298 let mut char_ix = 0;
299 let mut prev_utf16_ix = 0;
300
301 let mut runs = Vec::new();
302 for run in line.glyph_runs().into_iter() {
303 let font_id = font_cache.font_id_for_native_font(unsafe {
304 run.attributes()
305 .unwrap()
306 .get(kCTFontAttributeName)
307 .downcast::<CTFont>()
308 .unwrap()
309 });
310
311 let mut glyphs = Vec::new();
312 for ((glyph_id, position), utf16_ix) in run
313 .glyphs()
314 .iter()
315 .zip(run.positions().iter())
316 .zip(run.string_indices().iter())
317 {
318 let utf16_ix = usize::try_from(*utf16_ix).unwrap();
319 char_ix +=
320 char::decode_utf16(utf16_chars.by_ref().take(utf16_ix - prev_utf16_ix)).count();
321 prev_utf16_ix = utf16_ix;
322
323 glyphs.push(Glyph {
324 id: *glyph_id as GlyphId,
325 position: vec2f(position.x as f32, position.y as f32),
326 index: char_ix,
327 });
328 }
329
330 runs.push(Run { font_id, glyphs })
331 }
332
333 Line {
334 width,
335 runs,
336 font_size,
337 len: char_ix + 1,
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344 use anyhow::Result;
345 use font_kit::properties::{
346 Properties as FontProperties, Style as FontStyle, Weight as FontWeight,
347 };
348
349 #[test]
350 fn test_layout_str() -> Result<()> {
351 let mut font_cache = FontCache::new();
352 let menlo = font_cache.load_family(&["Menlo"])?;
353 let menlo_regular = font_cache.select_font(menlo, &FontProperties::new())?;
354 let menlo_italic =
355 font_cache.select_font(menlo, &FontProperties::new().style(FontStyle::Italic))?;
356 let menlo_bold =
357 font_cache.select_font(menlo, &FontProperties::new().weight(FontWeight::BOLD))?;
358
359 let line = layout_str(
360 "hello world π",
361 16.0,
362 &[
363 (0..2, menlo_bold),
364 (2..6, menlo_italic),
365 (6..13, menlo_regular),
366 ],
367 &mut font_cache,
368 );
369
370 assert!(font_cache.is_emoji(line.runs.last().unwrap().font_id));
371
372 Ok(())
373 }
374
375 #[test]
376 fn test_char_indices() -> Result<()> {
377 let mut font_cache = FontCache::new();
378 let zapfino = font_cache.load_family(&["Zapfino"])?;
379 let zapfino_regular = font_cache.select_font(zapfino, &FontProperties::new())?;
380 let menlo = font_cache.load_family(&["Menlo"])?;
381 let menlo_regular = font_cache.select_font(menlo, &FontProperties::new())?;
382
383 let text = "This is, mπre πr less, Zapfino!π";
384 let line = layout_str(
385 text,
386 16.0,
387 &[
388 (0..9, zapfino_regular),
389 (11..22, menlo_regular),
390 (22..text.encode_utf16().count(), zapfino_regular),
391 ],
392 &mut font_cache,
393 );
394 assert_eq!(
395 line.runs
396 .iter()
397 .flat_map(|r| r.glyphs.iter())
398 .map(|g| g.index)
399 .collect::<Vec<_>>(),
400 vec![
401 0, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
402 31, 32
403 ]
404 );
405 Ok(())
406 }
407}