1use crate::RenderError;
2use parley::fontique::Blob;
3use std::ops::Range;
4
5/// Text the renderer can paint, as glyph runs in layer units. Owners recover their
6/// concrete text from a primitive through `Any`.
7pub trait Glyphs: std::any::Any {
8 fn runs(
9 &self,
10 paint: &mut dyn FnMut(GlyphRun<'_>) -> Result<(), RenderError>,
11 ) -> Result<(), RenderError>;
12}
13
14/// Glyphs sharing one font, size and colour.
15pub struct GlyphRun<'a> {
16 pub font: &'a Blob<u8>,
17 /// The face within a font collection.
18 pub index: u32,
19 pub size: f32,
20 /// Variation coordinates in F2Dot14 bits.
21 pub coords: &'a [i16],
22 pub embolden: bool,
23 /// Synthesized oblique angle in degrees.
24 pub skew: Option<f32>,
25 /// `None` paints in the primitive's ink.
26 pub color: Option<[f32; 4]>,
27 /// The colour painted behind the glyphs, such as a highlight; `None` for the layer's
28 /// backdrop.
29 pub backdrop: Option<[f32; 4]>,
30 /// Top and height of the run's line: lines outside the target are skipped, and
31 /// colour glyphs shrink to fit.
32 pub line: [f32; 2],
33 pub glyphs: &'a mut dyn Iterator<Item = Glyph>,
34 /// Underline and strikethrough, painted after the glyphs.
35 pub decorations: &'a [Decoration],
36 /// The source the glyphs show, for a document's text layer; the renderer ignores it.
37 pub text: &'a str,
38 /// Each cluster's bytes of `text` and how many glyphs show it, in `glyphs`' order; a
39 /// ligature's later clusters take none.
40 pub clusters: &'a mut dyn Iterator<Item = (Range<usize>, usize)>,
41}
42
43/// A glyph's baseline position.
44#[derive(Clone, Copy)]
45pub struct Glyph {
46 pub id: u32,
47 pub x: f32,
48 pub y: f32,
49}
50
51/// A decoration line whose top edge is `y`; it stays at least one device pixel thick.
52#[derive(Clone, Copy)]
53pub struct Decoration {
54 pub x: f32,
55 pub y: f32,
56 pub width: f32,
57 pub thickness: f32,
58 /// `None` paints in the primitive's ink.
59 pub color: Option<[f32; 4]>,
60}
61
62/// Paints one parley glyph run of `text` laid out, with its line's baseline moved to
63/// `baseline`, its glyphs raised by `rise` and `backdrop` behind them.
64#[allow(clippy::too_many_arguments)]
65pub fn paint_parley_run<B: parley::Brush>(
66 run: &parley::GlyphRun<'_, B>,
67 text: &str,
68 baseline: f32,
69 rise: f32,
70 line: [f32; 2],
71 color: impl Fn(&B) -> Option<[f32; 4]>,
72 backdrop: Option<[f32; 4]>,
73 paint: &mut dyn FnMut(GlyphRun<'_>) -> Result<(), RenderError>,
74) -> Result<(), RenderError> {
75 let shaped = run.run();
76 let font = &shaped.font().font;
77 let coords: Vec<i16> = shaped
78 .normalized_coords()
79 .iter()
80 .map(|c| c.to_bits())
81 .collect();
82 let synthesis = shaped.synthesis();
83 let style = run.style();
84 let metrics = shaped.font_metrics();
85 let decorations: Vec<_> = [
86 (
87 &style.underline,
88 metrics.underline_offset,
89 metrics.underline_size,
90 ),
91 (
92 &style.strikethrough,
93 metrics.strikethrough_offset,
94 metrics.strikethrough_size,
95 ),
96 ]
97 .into_iter()
98 .filter_map(|(decoration, offset, thickness)| {
99 let decoration = decoration.as_ref()?;
100 Some(Decoration {
101 x: run.offset(),
102 y: baseline - rise - decoration.offset.unwrap_or(offset),
103 width: run.advance(),
104 thickness: decoration.size.unwrap_or(thickness),
105 color: color(&decoration.brush),
106 })
107 })
108 .collect();
109 let line_baseline = run.baseline();
110 let mut glyphs = run.positioned_glyphs().map(|glyph| Glyph {
111 id: glyph.id,
112 x: glyph.x,
113 y: glyph.y + baseline - line_baseline - rise,
114 });
115 // The run's clusters are those of its style within its span of the line; walked only
116 // when a text layer asks.
117 let [start, end] = [run.offset(), run.offset() + run.advance()];
118 let mut at = None;
119 let mut clusters = run.run().visual_clusters().filter_map(|cluster| {
120 let x = *at.get_or_insert_with(|| cluster.visual_offset().unwrap_or(start));
121 at = Some(x + cluster.advance());
122 let within = x > start - 0.01 && x < end - 0.01;
123 (cluster.style_index() == run.style_index() && within)
124 .then(|| (cluster.text_range(), cluster.glyphs().count()))
125 });
126 paint(GlyphRun {
127 font: &font.data,
128 index: font.index,
129 size: shaped.font_size(),
130 coords: &coords,
131 embolden: synthesis.embolden(),
132 skew: synthesis.skew(),
133 color: color(&style.brush),
134 backdrop,
135 line,
136 glyphs: &mut glyphs,
137 decorations: &decorations,
138 text,
139 clusters: &mut clusters,
140 })
141}