Skip to main content

pfx_text/
number.rs

1use std::hash::{Hash, Hasher};
2
3use cosmic_text::{Attrs, Buffer, LayoutGlyph, Metrics, Shaping, SubpixelBin, Wrap};
4
5use crate::{
6    Anchor, Error, Face, Pen, Placed, Placement, RasterKey, Representation, Span, Spot, TextEngine,
7    face_attrs, field_ppem, raster_flags, raster_key,
8};
9
10#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
11pub enum Figures {
12    #[default]
13    Tabular,
14    TabularDigits,
15    Proportional,
16}
17
18#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
19pub enum Ligatures {
20    #[default]
21    Kept,
22    Off,
23}
24
25#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
26pub struct NumberForms {
27    pub figures: Figures,
28    pub ligatures: Ligatures,
29}
30
31#[derive(Clone, Copy, Debug, PartialEq)]
32pub(crate) struct Columns {
33    widest: f32,
34    solo: [f32; 10],
35}
36
37struct NumberGlyph {
38    spot: Spot,
39    advance: f32,
40    coverage: [RasterKey; 4],
41    msdf: RasterKey,
42}
43
44pub(crate) struct NumberFace {
45    pub(crate) stamp: u64,
46    pub(crate) bytes: u64,
47    chars: Vec<char>,
48    glyphs: Vec<Option<NumberGlyph>>,
49    kern: Vec<Option<f32>>,
50    columns: Option<Columns>,
51    line_y: f32,
52    height: f32,
53}
54
55fn pair_sequence(count: usize) -> Vec<usize> {
56    let mut next = vec![0usize; count];
57    let mut stack = vec![0usize];
58    let mut circuit = Vec::with_capacity(count * count + 1);
59    while let Some(&node) = stack.last() {
60        if next[node] < count {
61            stack.push(next[node]);
62            next[node] += 1;
63        } else {
64            circuit.push(node);
65            stack.pop();
66        }
67    }
68    circuit.reverse();
69    circuit
70}
71
72fn face_key(face: &Face, chars: &str, scale: f32, forms: NumberForms) -> u64 {
73    let mut hasher = std::collections::hash_map::DefaultHasher::new();
74    chars.hash(&mut hasher);
75    face.family.hash(&mut hasher);
76    face.size.to_bits().hash(&mut hasher);
77    face.line.to_bits().hash(&mut hasher);
78    face.weight.hash(&mut hasher);
79    face.italic.hash(&mut hasher);
80    face.spacing.to_bits().hash(&mut hasher);
81    scale.to_bits().hash(&mut hasher);
82    forms.hash(&mut hasher);
83    hasher.finish()
84}
85
86fn valid(face: &Face, color: [f32; 4], scale: f32) -> bool {
87    scale.is_finite()
88        && scale > 0.0
89        && face.size.is_finite()
90        && face.size > 0.0
91        && face.line.is_finite()
92        && face.line > 0.0
93        && face.spacing.is_finite()
94        && color.iter().all(|c| c.is_finite())
95}
96
97fn same_glyph(solo: Option<&LayoutGlyph>, glyph: &LayoutGlyph) -> bool {
98    solo.is_some_and(|solo| solo.glyph_id == glyph.glyph_id && solo.font_id == glyph.font_id)
99}
100
101fn single(glyphs: Vec<LayoutGlyph>) -> Option<LayoutGlyph> {
102    <[LayoutGlyph; 1]>::try_from(glyphs)
103        .ok()
104        .map(|[glyph]| glyph)
105}
106
107pub(crate) fn char_lines(chars: &[char]) -> String {
108    chars
109        .iter()
110        .map(|c| c.to_string())
111        .collect::<Vec<_>>()
112        .join("\n")
113}
114
115pub(crate) fn unique_chars(chars: &str) -> Vec<char> {
116    let mut set = Vec::new();
117    for c in chars.chars() {
118        if !set.contains(&c) {
119            set.push(c);
120        }
121    }
122    set
123}
124
125pub(crate) fn bin_keys(key: &RasterKey) -> [RasterKey; 4] {
126    [
127        SubpixelBin::Zero,
128        SubpixelBin::One,
129        SubpixelBin::Two,
130        SubpixelBin::Three,
131    ]
132    .map(|x_bin| RasterKey {
133        x_bin,
134        ..key.clone()
135    })
136}
137
138fn unwrapped(fonts: &mut cosmic_text::FontSystem, face: &Face, scale: f32) -> Buffer {
139    let mut buffer = Buffer::new(fonts, Metrics::new(face.size * scale, face.line * scale));
140    buffer.set_wrap(fonts, Wrap::None);
141    buffer.set_size(fonts, None, None);
142    buffer
143}
144
145fn shape_lines(
146    fonts: &mut cosmic_text::FontSystem,
147    buffer: &mut Buffer,
148    text: &str,
149    attrs: &Attrs,
150) -> Vec<(f32, f32, Vec<LayoutGlyph>)> {
151    buffer.set_text(fonts, text, attrs, Shaping::Advanced, None);
152    buffer.shape_until_scroll(fonts, false);
153    let mut lines = vec![(0.0, 0.0, Vec::new()); text.split('\n').count()];
154    for run in buffer.layout_runs() {
155        if let Some(line) = lines.get_mut(run.line_i) {
156            *line = (
157                run.line_y - run.line_top,
158                run.line_height,
159                run.glyphs.to_vec(),
160            );
161        }
162    }
163    lines
164}
165
166impl TextEngine {
167    pub fn place_number(
168        &mut self,
169        span: &Span,
170        chars: &str,
171        anchor: Anchor,
172        placement: &Placement,
173    ) -> Result<Placed, Error> {
174        self.place_number_with(span, chars, NumberForms::default(), anchor, placement)
175    }
176
177    pub fn place_number_with(
178        &mut self,
179        span: &Span,
180        chars: &str,
181        forms: NumberForms,
182        anchor: Anchor,
183        placement: &Placement,
184    ) -> Result<Placed, Error> {
185        if !valid(&span.face, span.color, placement.scale) {
186            return Err(Error::InvalidStyle);
187        }
188        let mut columns = None;
189        if !span.text.is_empty() && span.text.chars().all(|c| chars.contains(c)) {
190            let key = self.number_face(&span.face, chars, placement.scale, forms)?;
191            if let Some(face) = self.numbers.remove(&key) {
192                let indices = span
193                    .text
194                    .chars()
195                    .filter_map(|c| face.chars.iter().position(|&known| known == c))
196                    .collect::<Vec<_>>();
197                let placed = self.place_fast(&face, &indices, span.color, anchor, placement);
198                columns = face.columns;
199                self.numbers.insert(key, face);
200                if let Some(placed) = placed? {
201                    self.shared.stats.fast += 1;
202                    return Ok(placed);
203                }
204            }
205        } else if forms.figures != Figures::Proportional
206            && span.text.bytes().any(|b| b.is_ascii_digit())
207        {
208            columns = self
209                .number_face(&span.face, chars, placement.scale, forms)
210                .ok()
211                .and_then(|key| self.numbers[&key].columns);
212        }
213        let spans = std::slice::from_ref(span);
214        let key = self.layout_block(
215            spans,
216            None,
217            placement.scale,
218            Anchor::Start,
219            Some(forms),
220            columns,
221        )?;
222        let width = self.blocks[&key].width;
223        let placement = Placement {
224            origin: anchored(placement.origin, width, anchor),
225            ..*placement
226        };
227        self.place_block(key, spans, &placement)
228    }
229
230    pub fn cached_number_faces(&self) -> usize {
231        self.numbers.len()
232    }
233
234    pub fn ligating_pairs(
235        &mut self,
236        face: &Face,
237        chars: &str,
238        forms: NumberForms,
239        scale: f32,
240    ) -> Result<Vec<(char, char)>, Error> {
241        if !valid(face, [0.0; 4], scale) {
242            return Err(Error::InvalidStyle);
243        }
244        let key = self.number_face(face, chars, scale, forms)?;
245        let face = &self.numbers[&key];
246        let count = face.chars.len();
247        let mut pairs = Vec::new();
248        for i in 0..count {
249            for j in 0..count {
250                if face.glyphs[i].is_some()
251                    && face.glyphs[j].is_some()
252                    && face.kern[i * count + j].is_none()
253                {
254                    pairs.push((face.chars[i], face.chars[j]));
255                }
256            }
257        }
258        Ok(pairs)
259    }
260
261    fn place_fast(
262        &mut self,
263        face: &NumberFace,
264        indices: &[usize],
265        color: [f32; 4],
266        anchor: Anchor,
267        placement: &Placement,
268    ) -> Result<Option<Placed>, Error> {
269        let count = face.chars.len();
270        if indices.iter().any(|&i| face.glyphs[i].is_none())
271            || indices
272                .windows(2)
273                .any(|pair| face.kern[pair[0] * count + pair[1]].is_none())
274        {
275            return Ok(None);
276        }
277        let mut pens = Vec::with_capacity(indices.len());
278        let mut x = 0.0f32;
279        for (at, &i) in indices.iter().enumerate() {
280            let glyph = face.glyphs[i].as_ref().unwrap();
281            pens.push(x + glyph.spot.x);
282            x += glyph.advance;
283            if let Some(&next) = indices.get(at + 1) {
284                x += if face.columns.is_some()
285                    && (face.chars[i].is_ascii_digit() || face.chars[next].is_ascii_digit())
286                {
287                    0.0
288                } else {
289                    face.kern[i * count + next].unwrap_or_default()
290                };
291            }
292        }
293        let scale = placement.scale;
294        let placement = Placement {
295            origin: anchored(placement.origin, x / scale, anchor),
296            ..*placement
297        };
298        let mut quads = Vec::with_capacity(indices.len());
299        for (&i, pen_x) in indices.iter().zip(pens) {
300            let glyph = face.glyphs[i].as_ref().unwrap();
301            let spot = Spot {
302                x: pen_x,
303                color,
304                ..glyph.spot.clone()
305            };
306            let pen = Pen::new(&spot, &placement);
307            let key = match placement.representation {
308                Representation::Coverage => &glyph.coverage[pen.bin as usize],
309                Representation::Msdf => &glyph.msdf,
310            };
311            self.place_glyph(&spot, key, pen, &placement, &mut quads)?;
312        }
313        Ok(Some(Placed {
314            quads,
315            width: x / scale,
316            height: face.height / scale,
317            baseline: face.line_y / scale,
318        }))
319    }
320
321    pub(crate) fn column_shifts(
322        &mut self,
323        buffer: &Buffer,
324        columns: Columns,
325        face: &Face,
326        scale: f32,
327        forms: NumberForms,
328    ) -> (Vec<f32>, f32) {
329        let mut lines = Vec::new();
330        let mut wanted: Vec<char> = Vec::new();
331        for run in buffer.layout_runs() {
332            let glyphs = run
333                .glyphs
334                .iter()
335                .map(|glyph| {
336                    let mut chars = run.text[glyph.start..glyph.end].chars();
337                    let one = chars.next().filter(|_| chars.next().is_none());
338                    (one, glyph.w, glyph.glyph_id, glyph.font_id)
339                })
340                .collect::<Vec<_>>();
341            for pair in glyphs.windows(2) {
342                if let (Some(c), Some(next)) = (pair[0].0, pair[1].0)
343                    && !c.is_ascii_digit()
344                    && next.is_ascii_digit()
345                    && !wanted.contains(&c)
346                {
347                    wanted.push(c);
348                }
349            }
350            lines.push((run.line_w, glyphs));
351        }
352        let mut solo = Vec::new();
353        if !wanted.is_empty() {
354            let attrs = face_attrs(face, scale, 0, Some(forms));
355            let mut alone = unwrapped(&mut self.fonts, face, scale);
356            let text = char_lines(&wanted);
357            solo = shape_lines(&mut self.fonts, &mut alone, &text, &attrs)
358                .into_iter()
359                .map(|(_, _, glyphs)| single(glyphs))
360                .collect();
361        }
362        let digit = |c: Option<char>| c.and_then(|c| c.to_digit(10)).map(|d| d as usize);
363        let mut shifts = Vec::new();
364        let mut width = 0.0f32;
365        for (line_w, glyphs) in lines {
366            let mut shift = 0.0f32;
367            for (at, &(c, w, glyph_id, font_id)) in glyphs.iter().enumerate() {
368                let next = glyphs.get(at + 1).and_then(|next| digit(next.0));
369                let target = match digit(c) {
370                    Some(d) => {
371                        shifts.push(shift + (columns.widest - columns.solo[d]) * 0.5);
372                        columns.widest
373                    }
374                    None => {
375                        shifts.push(shift);
376                        c.filter(|_| next.is_some())
377                            .and_then(|c| wanted.iter().position(|&known| known == c))
378                            .and_then(|at| solo.get(at).and_then(Option::as_ref))
379                            .filter(|alone| alone.glyph_id == glyph_id && alone.font_id == font_id)
380                            .map_or(w, |alone| alone.w)
381                    }
382                };
383                shift += target - w;
384            }
385            width = width.max(line_w + shift);
386        }
387        (shifts, width)
388    }
389
390    pub(crate) fn number_glyph_keys(
391        &mut self,
392        face: &Face,
393        chars: &str,
394        scale: f32,
395        forms: NumberForms,
396        representation: Representation,
397    ) -> Result<Vec<(Spot, Vec<RasterKey>)>, Error> {
398        if !valid(face, [0.0; 4], scale) {
399            return Err(Error::InvalidStyle);
400        }
401        let key = self.number_face(face, chars, scale, forms)?;
402        let face = &self.numbers[&key];
403        Ok(face
404            .glyphs
405            .iter()
406            .flatten()
407            .map(|glyph| {
408                let keys = match representation {
409                    Representation::Coverage => glyph.coverage.to_vec(),
410                    Representation::Msdf => vec![glyph.msdf.clone()],
411                };
412                (glyph.spot.clone(), keys)
413            })
414            .collect())
415    }
416
417    pub(crate) fn number_face(
418        &mut self,
419        face: &Face,
420        chars: &str,
421        scale: f32,
422        forms: NumberForms,
423    ) -> Result<u64, Error> {
424        let key = face_key(face, chars, scale, forms);
425        let stamp = self.stamp();
426        if let Some(found) = self.numbers.get_mut(&key) {
427            found.stamp = stamp;
428            return Ok(key);
429        }
430        self.shared.stats.shaped += 1;
431        let set = unique_chars(chars);
432        let count = set.len();
433        let mut solo_chars = set.clone();
434        for digit in '0'..='9' {
435            if !solo_chars.contains(&digit) {
436                solo_chars.push(digit);
437            }
438        }
439        let attrs = face_attrs(face, scale, 0, Some(forms));
440        let mut buffer = unwrapped(&mut self.fonts, face, scale);
441        let solo = char_lines(&solo_chars);
442        let lines = shape_lines(&mut self.fonts, &mut buffer, &solo, &attrs);
443        let (line_y, height) = lines
444            .iter()
445            .find(|line| line.1 > 0.0)
446            .map(|line| (line.0, line.1))
447            .ok_or(Error::UnsupportedGlyph)?;
448        let found = lines
449            .into_iter()
450            .map(|(_, _, glyphs)| single(glyphs))
451            .collect::<Vec<_>>();
452        let digits = ('0'..='9')
453            .map(|digit| {
454                solo_chars
455                    .iter()
456                    .position(|&c| c == digit)
457                    .and_then(|at| found[at].as_ref())
458                    .map(|glyph| glyph.w)
459            })
460            .collect::<Option<Vec<_>>>();
461        let columns = digits.and_then(|digits| {
462            let widest = digits.iter().copied().fold(0.0f32, f32::max);
463            (forms.figures != Figures::Proportional
464                && digits.iter().any(|&w| (w - widest).abs() > 1e-3))
465            .then(|| Columns {
466                widest,
467                solo: std::array::from_fn(|d| digits[d]),
468            })
469        });
470        let found = &found[..count];
471        let kern = self.pair_kerns(&mut buffer, &attrs, &set, found);
472        let mut glyphs = Vec::with_capacity(count);
473        for (i, glyph) in found.iter().enumerate() {
474            let Some(glyph) = glyph else {
475                glyphs.push(None);
476                continue;
477            };
478            let font = self
479                .fonts
480                .get_font(glyph.font_id, glyph.font_weight)
481                .ok_or(Error::UnsupportedGlyph)?;
482            let flags = raster_flags(
483                glyph.cache_key_flags,
484                face.italic,
485                self.fonts.db().face(glyph.font_id).map(|face| face.style),
486            );
487            let template = |ppem: f32, representation: Representation| RasterKey {
488                representation,
489                ..raster_key(
490                    glyph.font_id,
491                    glyph.glyph_id,
492                    ppem,
493                    glyph.font_weight,
494                    glyph.font_size / scale,
495                    flags,
496                    font.as_swash(),
497                )
498            };
499            let coverage = template(glyph.font_size, Representation::Coverage);
500            let bins = bin_keys(&coverage);
501            let msdf = template(field_ppem(glyph.font_size), Representation::Msdf);
502            let column = columns.filter(|_| set[i].is_ascii_digit());
503            let pad = column.map_or(0.0, |columns| (columns.widest - glyph.w) * 0.5);
504            glyphs.push(Some(NumberGlyph {
505                spot: Spot {
506                    font_id: glyph.font_id,
507                    font_weight: glyph.font_weight,
508                    glyph_id: glyph.glyph_id,
509                    flags,
510                    font_size: glyph.font_size,
511                    x: glyph.x_offset * glyph.font_size + pad,
512                    y: glyph.y - glyph.y_offset * glyph.font_size,
513                    line_y,
514                    line_index: 0,
515                    color: [0.0; 4],
516                },
517                advance: column.map_or(glyph.w, |columns| columns.widest),
518                coverage: bins,
519                msdf,
520            }));
521        }
522        let bytes = (std::mem::size_of::<NumberFace>()
523            + count * std::mem::size_of::<char>()
524            + kern.len() * std::mem::size_of::<Option<f32>>()
525            + glyphs.len() * std::mem::size_of::<Option<NumberGlyph>>()
526            + glyphs
527                .iter()
528                .flatten()
529                .map(|glyph| {
530                    glyph
531                        .coverage
532                        .iter()
533                        .chain([&glyph.msdf])
534                        .map(|key| key.font_id.capacity())
535                        .sum::<usize>()
536                })
537                .sum::<usize>()) as u64;
538        self.numbers.insert(
539            key,
540            NumberFace {
541                stamp,
542                bytes,
543                chars: set,
544                glyphs,
545                kern,
546                columns,
547                line_y,
548                height,
549            },
550        );
551        Ok(key)
552    }
553
554    fn pair_kerns(
555        &mut self,
556        buffer: &mut Buffer,
557        attrs: &Attrs,
558        set: &[char],
559        solo: &[Option<LayoutGlyph>],
560    ) -> Vec<Option<f32>> {
561        let count = set.len();
562        let mut kern = vec![None; count * count];
563        if count == 0 {
564            return kern;
565        }
566        let order = pair_sequence(count);
567        let mut starts = Vec::with_capacity(order.len());
568        let mut sequence = String::new();
569        for &i in &order {
570            starts.push(sequence.len());
571            sequence.push(set[i]);
572        }
573        let laid = shape_lines(&mut self.fonts, buffer, &sequence, attrs);
574        let mut cover = vec![0u32; order.len()];
575        let mut x = vec![None; order.len()];
576        if let [(_, _, glyphs)] = laid.as_slice() {
577            for glyph in glyphs {
578                let Ok(first) = starts.binary_search(&glyph.start) else {
579                    continue;
580                };
581                let last = starts.partition_point(|&start| start < glyph.end);
582                for slot in &mut cover[first..last.max(first + 1)] {
583                    *slot += 1;
584                }
585                if last == first + 1 && same_glyph(solo[order[first]].as_ref(), glyph) {
586                    x[first] = Some(glyph.x);
587                }
588            }
589        }
590        let clean = |at: usize| if cover[at] == 1 { x[at] } else { None };
591        let advance = |i: usize| solo[i].as_ref().map_or(0.0, |glyph| glyph.w);
592        let mut unresolved = Vec::new();
593        for (at, pair) in order.windows(2).enumerate() {
594            match (clean(at), clean(at + 1)) {
595                (Some(a), Some(b)) => {
596                    kern[pair[0] * count + pair[1]] = Some(b - a - advance(pair[0]))
597                }
598                _ => unresolved.push((pair[0], pair[1])),
599            }
600        }
601        if unresolved.is_empty() {
602            return kern;
603        }
604        let text = unresolved
605            .iter()
606            .map(|&(i, j)| format!("{}{}", set[i], set[j]))
607            .collect::<Vec<_>>()
608            .join("\n");
609        let lines = shape_lines(&mut self.fonts, buffer, &text, attrs);
610        for (&(i, j), (_, _, glyphs)) in unresolved.iter().zip(lines) {
611            if let [a, b] = glyphs.as_slice()
612                && a.start == 0
613                && b.start == set[i].len_utf8()
614                && same_glyph(solo[i].as_ref(), a)
615                && same_glyph(solo[j].as_ref(), b)
616            {
617                kern[i * count + j] = Some(b.x - a.x - advance(i));
618            }
619        }
620        kern
621    }
622}
623
624pub(crate) fn anchored(origin: [f32; 2], width: f32, anchor: Anchor) -> [f32; 2] {
625    match anchor {
626        Anchor::Start => origin,
627        Anchor::Center => [origin[0] - width * 0.5, origin[1]],
628        Anchor::End => [origin[0] - width, origin[1]],
629    }
630}
631
632#[cfg(test)]
633mod tests {
634    use super::*;
635
636    #[test]
637    fn the_pair_sequence_holds_every_ordered_pair_once() {
638        let count = 17;
639        let order = pair_sequence(count);
640        assert_eq!(order.len(), count * count + 1);
641        let mut seen = vec![0; count * count];
642        for pair in order.windows(2) {
643            seen[pair[0] * count + pair[1]] += 1;
644        }
645        assert!(seen.iter().all(|&count| count == 1));
646    }
647}