swash/shape/
partition.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*!
Shaper that partitions by font using trait based per-cluster selection.

*/

use super::{Direction, ShapeContext, Shaper};
use crate::text::cluster::{CharCluster, Parser, Token};
use crate::text::{Codepoint as _, Language, Script};
use crate::{FontRef, Setting, Synthesis};
use core::iter::Copied;
use core::slice;

/// Trait for types that can select appropriate fonts for character clusters.
pub trait Selector {
    /// Type of font returned by the provider.
    type SelectedFont: SelectedFont;

    /// Selects a font for the specified character cluster.
    fn select_font(&mut self, cluster: &mut CharCluster) -> Option<Self::SelectedFont>;
}

/// Trait for a font provided by a font selector.
pub trait SelectedFont: PartialEq {
    /// Returns a reference to the underlying font.
    fn font(&self) -> FontRef;

    fn id_override(&self) -> Option<[u64; 2]> {
        None
    }

    /// Returns an optional synthesis state for the font.
    fn synthesis(&self) -> Option<Synthesis> {
        None
    }
}

/// Trait for types that specify shaping options.
pub trait ShapeOptions {
    /// Iterator over the feature settings for a fragment.
    type Features: Iterator<Item = Setting<u16>>;

    /// Iterator over the variation settings for a fragment.
    type Variations: Iterator<Item = Setting<f32>>;

    /// Returns the script of the text in the fragment.
    fn script(&self) -> Script {
        Script::Latin
    }

    /// Returns the language of the text in the fragment.
    fn language(&self) -> Option<Language> {
        None
    }

    /// Returns the direction for the fragment.
    fn direction(&self) -> Direction {
        Direction::LeftToRight
    }

    /// Returns the font size for the fragment.
    fn size(&self) -> f32 {
        0.
    }

    /// Returns an iterator over the feature settings for the fragment.
    fn features(&self) -> Self::Features;

    /// Returns an iterator over the variation settings for the fragment.
    fn variations(&self) -> Self::Variations;

    /// Returns true if the shaper should insert dotted circles for broken
    /// clusters in the fragment.
    fn insert_dotted_circles(&self) -> bool {
        false
    }
}

/// Simple implementation of the shape options trait.
#[derive(Copy, Clone)]
pub struct SimpleShapeOptions<'a> {
    /// Script for the fragment.
    pub script: Script,
    /// Language for the fragment.
    pub language: Option<Language>,
    /// Text direction of the fragment.
    pub direction: Direction,
    /// Font size for the fragment.
    pub size: f32,
    /// Feature settings for the fragment.
    pub features: &'a [Setting<u16>],
    /// Variation settings for the fragment.
    pub variations: &'a [Setting<f32>],
    /// True if the shaper should insert dotted circles for broken clusters.
    pub insert_dotted_circles: bool,
}

impl Default for SimpleShapeOptions<'_> {
    fn default() -> Self {
        Self {
            script: Script::Latin,
            language: None,
            direction: Direction::LeftToRight,
            size: 0.,
            features: &[],
            variations: &[],
            insert_dotted_circles: false,
        }
    }
}

impl<'a> ShapeOptions for SimpleShapeOptions<'a> {
    type Features = Copied<slice::Iter<'a, Setting<u16>>>;
    type Variations = Copied<slice::Iter<'a, Setting<f32>>>;

    fn script(&self) -> Script {
        self.script
    }

    fn language(&self) -> Option<Language> {
        self.language
    }

    fn direction(&self) -> Direction {
        self.direction
    }

    fn size(&self) -> f32 {
        self.size
    }

    fn features(&self) -> Self::Features {
        self.features.iter().copied()
    }

    fn variations(&self) -> Self::Variations {
        self.variations.iter().copied()
    }

    fn insert_dotted_circles(&self) -> bool {
        self.insert_dotted_circles
    }
}

/// Shapes a run of text (provided as a [`Token`] iterator) using the specified [`Selector`] to assign
/// per-cluster fonts. Invokes the specified closure `f` for each contiguous fragment with the chosen font
/// and a prepared [`Shaper`] that contains the cluster content of the fragment.
pub fn shape<S, T>(
    context: &mut ShapeContext,
    selector: &mut S,
    options: &impl ShapeOptions,
    tokens: T,
    mut f: impl FnMut(&S::SelectedFont, Shaper),
) where
    S: Selector,
    T: IntoIterator<Item = Token>,
    T::IntoIter: Clone,
{
    let mut cluster = CharCluster::new();
    if options.direction() == Direction::RightToLeft {
        let mut parser = Parser::new(
            options.script(),
            tokens.into_iter().map(|mut t| {
                t.ch = t.ch.mirror().unwrap_or(t.ch);
                t
            }),
        );
        if !parser.next(&mut cluster) {
            return;
        }
        let mut font = selector.select_font(&mut cluster);
        while shape_clusters(
            context,
            selector,
            options,
            &mut parser,
            &mut cluster,
            &mut font,
            &mut f,
        ) {}
    } else {
        let mut parser = Parser::new(options.script(), tokens.into_iter());
        if !parser.next(&mut cluster) {
            return;
        }
        let mut font = selector.select_font(&mut cluster);
        while shape_clusters(
            context,
            selector,
            options,
            &mut parser,
            &mut cluster,
            &mut font,
            &mut f,
        ) {}
    }
}

fn shape_clusters<S, T>(
    context: &mut ShapeContext,
    selector: &mut S,
    options: &impl ShapeOptions,
    parser: &mut Parser<T>,
    cluster: &mut CharCluster,
    current_font: &mut Option<S::SelectedFont>,
    f: &mut impl FnMut(&S::SelectedFont, Shaper),
) -> bool
where
    S: Selector,
    T: Iterator<Item = Token> + Clone,
{
    if current_font.is_none() {
        return false;
    }
    let font = current_font.as_ref().unwrap();
    let font_ref = font.font();
    let id = font
        .id_override()
        .unwrap_or([font_ref.key.value(), u64::MAX]);
    let mut shaper = context
        .builder_with_id(font.font(), id)
        .script(options.script())
        .language(options.language())
        .direction(options.direction())
        .size(options.size())
        .features(options.features())
        .variations(
            font.synthesis()
                .as_ref()
                .map(|s| s.variations())
                .unwrap_or(&[])
                .iter()
                .copied(),
        )
        .variations(options.variations())
        .insert_dotted_circles(options.insert_dotted_circles())
        .build();
    loop {
        shaper.add_cluster(cluster);
        if !parser.next(cluster) {
            f(font, shaper);
            return false;
        }
        if let Some(next_font) = selector.select_font(cluster) {
            if next_font != *font {
                f(font, shaper);
                *current_font = Some(next_font);
                return true;
            }
        } else {
            return false;
        }
    }
}