swash/shape/
partition.rs

1/*!
2Shaper that partitions by font using trait based per-cluster selection.
3
4*/
5
6use super::{Direction, ShapeContext, Shaper};
7use crate::text::cluster::{CharCluster, Parser, Token};
8use crate::text::{Codepoint as _, Language, Script};
9use crate::{FontRef, Setting, Synthesis};
10use core::iter::Copied;
11use core::slice;
12
13/// Trait for types that can select appropriate fonts for character clusters.
14pub trait Selector {
15    /// Type of font returned by the provider.
16    type SelectedFont: SelectedFont;
17
18    /// Selects a font for the specified character cluster.
19    fn select_font(&mut self, cluster: &mut CharCluster) -> Option<Self::SelectedFont>;
20}
21
22/// Trait for a font provided by a font selector.
23pub trait SelectedFont: PartialEq {
24    /// Returns a reference to the underlying font.
25    fn font(&self) -> FontRef;
26
27    fn id_override(&self) -> Option<[u64; 2]> {
28        None
29    }
30
31    /// Returns an optional synthesis state for the font.
32    fn synthesis(&self) -> Option<Synthesis> {
33        None
34    }
35}
36
37/// Trait for types that specify shaping options.
38pub trait ShapeOptions {
39    /// Iterator over the feature settings for a fragment.
40    type Features: Iterator<Item = Setting<u16>>;
41
42    /// Iterator over the variation settings for a fragment.
43    type Variations: Iterator<Item = Setting<f32>>;
44
45    /// Returns the script of the text in the fragment.
46    fn script(&self) -> Script {
47        Script::Latin
48    }
49
50    /// Returns the language of the text in the fragment.
51    fn language(&self) -> Option<Language> {
52        None
53    }
54
55    /// Returns the direction for the fragment.
56    fn direction(&self) -> Direction {
57        Direction::LeftToRight
58    }
59
60    /// Returns the font size for the fragment.
61    fn size(&self) -> f32 {
62        0.
63    }
64
65    /// Returns an iterator over the feature settings for the fragment.
66    fn features(&self) -> Self::Features;
67
68    /// Returns an iterator over the variation settings for the fragment.
69    fn variations(&self) -> Self::Variations;
70
71    /// Returns true if the shaper should insert dotted circles for broken
72    /// clusters in the fragment.
73    fn insert_dotted_circles(&self) -> bool {
74        false
75    }
76}
77
78/// Simple implementation of the shape options trait.
79#[derive(Copy, Clone)]
80pub struct SimpleShapeOptions<'a> {
81    /// Script for the fragment.
82    pub script: Script,
83    /// Language for the fragment.
84    pub language: Option<Language>,
85    /// Text direction of the fragment.
86    pub direction: Direction,
87    /// Font size for the fragment.
88    pub size: f32,
89    /// Feature settings for the fragment.
90    pub features: &'a [Setting<u16>],
91    /// Variation settings for the fragment.
92    pub variations: &'a [Setting<f32>],
93    /// True if the shaper should insert dotted circles for broken clusters.
94    pub insert_dotted_circles: bool,
95}
96
97impl Default for SimpleShapeOptions<'_> {
98    fn default() -> Self {
99        Self {
100            script: Script::Latin,
101            language: None,
102            direction: Direction::LeftToRight,
103            size: 0.,
104            features: &[],
105            variations: &[],
106            insert_dotted_circles: false,
107        }
108    }
109}
110
111impl<'a> ShapeOptions for SimpleShapeOptions<'a> {
112    type Features = Copied<slice::Iter<'a, Setting<u16>>>;
113    type Variations = Copied<slice::Iter<'a, Setting<f32>>>;
114
115    fn script(&self) -> Script {
116        self.script
117    }
118
119    fn language(&self) -> Option<Language> {
120        self.language
121    }
122
123    fn direction(&self) -> Direction {
124        self.direction
125    }
126
127    fn size(&self) -> f32 {
128        self.size
129    }
130
131    fn features(&self) -> Self::Features {
132        self.features.iter().copied()
133    }
134
135    fn variations(&self) -> Self::Variations {
136        self.variations.iter().copied()
137    }
138
139    fn insert_dotted_circles(&self) -> bool {
140        self.insert_dotted_circles
141    }
142}
143
144/// Shapes a run of text (provided as a [`Token`] iterator) using the specified [`Selector`] to assign
145/// per-cluster fonts. Invokes the specified closure `f` for each contiguous fragment with the chosen font
146/// and a prepared [`Shaper`] that contains the cluster content of the fragment.
147pub fn shape<S, T>(
148    context: &mut ShapeContext,
149    selector: &mut S,
150    options: &impl ShapeOptions,
151    tokens: T,
152    mut f: impl FnMut(&S::SelectedFont, Shaper),
153) where
154    S: Selector,
155    T: IntoIterator<Item = Token>,
156    T::IntoIter: Clone,
157{
158    let mut cluster = CharCluster::new();
159    if options.direction() == Direction::RightToLeft {
160        let mut parser = Parser::new(
161            options.script(),
162            tokens.into_iter().map(|mut t| {
163                t.ch = t.ch.mirror().unwrap_or(t.ch);
164                t
165            }),
166        );
167        if !parser.next(&mut cluster) {
168            return;
169        }
170        let mut font = selector.select_font(&mut cluster);
171        while shape_clusters(
172            context,
173            selector,
174            options,
175            &mut parser,
176            &mut cluster,
177            &mut font,
178            &mut f,
179        ) {}
180    } else {
181        let mut parser = Parser::new(options.script(), tokens.into_iter());
182        if !parser.next(&mut cluster) {
183            return;
184        }
185        let mut font = selector.select_font(&mut cluster);
186        while shape_clusters(
187            context,
188            selector,
189            options,
190            &mut parser,
191            &mut cluster,
192            &mut font,
193            &mut f,
194        ) {}
195    }
196}
197
198fn shape_clusters<S, T>(
199    context: &mut ShapeContext,
200    selector: &mut S,
201    options: &impl ShapeOptions,
202    parser: &mut Parser<T>,
203    cluster: &mut CharCluster,
204    current_font: &mut Option<S::SelectedFont>,
205    f: &mut impl FnMut(&S::SelectedFont, Shaper),
206) -> bool
207where
208    S: Selector,
209    T: Iterator<Item = Token> + Clone,
210{
211    if current_font.is_none() {
212        return false;
213    }
214    let font = current_font.as_ref().unwrap();
215    let font_ref = font.font();
216    let id = font
217        .id_override()
218        .unwrap_or([font_ref.key.value(), u64::MAX]);
219    let mut shaper = context
220        .builder_with_id(font.font(), id)
221        .script(options.script())
222        .language(options.language())
223        .direction(options.direction())
224        .size(options.size())
225        .features(options.features())
226        .variations(
227            font.synthesis()
228                .as_ref()
229                .map(|s| s.variations())
230                .unwrap_or(&[])
231                .iter()
232                .copied(),
233        )
234        .variations(options.variations())
235        .insert_dotted_circles(options.insert_dotted_circles())
236        .build();
237    loop {
238        shaper.add_cluster(cluster);
239        if !parser.next(cluster) {
240            f(font, shaper);
241            return false;
242        }
243        if let Some(next_font) = selector.select_font(cluster) {
244            if next_font != *font {
245                f(font, shaper);
246                *current_font = Some(next_font);
247                return true;
248            }
249        } else {
250            return false;
251        }
252    }
253}