text_layout.rs

  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}