allsorts_subset_browser/scripts/
syriac.rs1use unicode_joining_type::{get_joining_group, get_joining_type, JoiningGroup, JoiningType};
7
8use crate::error::{ParseError, ShapingError};
9use crate::gsub::{self, FeatureMask, GlyphData, GlyphOrigin, RawGlyph};
10use crate::layout::{FeatureTableSubstitution, GDEFTable, LayoutCache, LayoutTable, GSUB};
11use crate::tag;
12
13#[derive(Clone)]
14struct SyriacData {
15 joining_group: JoiningGroup,
16 joining_type: JoiningType,
17 feature_tag: u32,
18}
19
20impl GlyphData for SyriacData {
21 fn merge(data1: SyriacData, _data2: SyriacData) -> SyriacData {
22 data1
24 }
25}
26
27type SyriacGlyph = RawGlyph<SyriacData>;
29
30impl SyriacGlyph {
31 fn is_alaph(&self) -> bool {
32 self.extra_data.joining_group == JoiningGroup::Alaph
33 }
34
35 fn is_dalath_rish(&self) -> bool {
36 self.extra_data.joining_group == JoiningGroup::DalathRish
37 }
38
39 fn is_transparent(&self) -> bool {
40 self.extra_data.joining_type == JoiningType::Transparent || self.multi_subst_dup()
41 }
42
43 fn is_non_joining(&self) -> bool {
44 self.extra_data.joining_type == JoiningType::NonJoining
45 }
46
47 fn is_left_joining(&self) -> bool {
48 self.extra_data.joining_type == JoiningType::LeftJoining
49 || self.extra_data.joining_type == JoiningType::DualJoining
50 || self.extra_data.joining_type == JoiningType::JoinCausing
51 }
52
53 fn is_right_joining(&self) -> bool {
54 self.extra_data.joining_type == JoiningType::RightJoining
55 || self.extra_data.joining_type == JoiningType::DualJoining
56 || self.extra_data.joining_type == JoiningType::JoinCausing
57 }
58
59 fn feature_tag(&self) -> u32 {
60 self.extra_data.feature_tag
61 }
62
63 fn set_feature_tag(&mut self, feature_tag: u32) {
64 self.extra_data.feature_tag = feature_tag
65 }
66}
67
68impl From<&RawGlyph<()>> for SyriacGlyph {
69 fn from(raw_glyph: &RawGlyph<()>) -> SyriacGlyph {
70 let joining_type = match raw_glyph.glyph_origin {
74 GlyphOrigin::Char(c) => get_joining_type(c),
75 GlyphOrigin::Direct => JoiningType::NonJoining,
76 };
77
78 let joining_group = match raw_glyph.glyph_origin {
80 GlyphOrigin::Char(c) => get_joining_group(c),
81 GlyphOrigin::Direct => JoiningGroup::NoJoiningGroup,
82 };
83
84 SyriacGlyph {
85 unicodes: raw_glyph.unicodes.clone(),
86 glyph_index: raw_glyph.glyph_index,
87 liga_component_pos: raw_glyph.liga_component_pos,
88 glyph_origin: raw_glyph.glyph_origin,
89 flags: raw_glyph.flags,
90 variation: raw_glyph.variation,
91 extra_data: SyriacData {
92 joining_group,
93 joining_type,
94 feature_tag: tag::ISOL,
97 },
98 }
99 }
100}
101
102impl From<&SyriacGlyph> for RawGlyph<()> {
103 fn from(syriac_glyph: &SyriacGlyph) -> RawGlyph<()> {
104 RawGlyph {
105 unicodes: syriac_glyph.unicodes.clone(),
106 glyph_index: syriac_glyph.glyph_index,
107 liga_component_pos: syriac_glyph.liga_component_pos,
108 glyph_origin: syriac_glyph.glyph_origin,
109 flags: syriac_glyph.flags,
110 variation: syriac_glyph.variation,
111 extra_data: (),
112 }
113 }
114}
115
116pub fn gsub_apply_syriac(
117 gsub_cache: &LayoutCache<GSUB>,
118 gsub_table: &LayoutTable<GSUB>,
119 gdef_table: Option<&GDEFTable>,
120 script_tag: u32,
121 lang_tag: Option<u32>,
122 feature_variations: Option<&FeatureTableSubstitution<'_>>,
123 raw_glyphs: &mut Vec<RawGlyph<()>>,
124) -> Result<(), ShapingError> {
125 match gsub_table.find_script(script_tag)? {
126 Some(s) => {
127 if s.find_langsys_or_default(lang_tag)?.is_none() {
128 return Ok(());
129 }
130 }
131 None => return Ok(()),
132 }
133
134 let syriac_glyphs: &mut Vec<SyriacGlyph> =
135 &mut raw_glyphs.iter().map(SyriacGlyph::from).collect();
136
137 apply_lookups(
140 FeatureMask::CCMP,
141 gsub_cache,
142 gsub_table,
143 gdef_table,
144 script_tag,
145 lang_tag,
146 feature_variations,
147 syriac_glyphs,
148 |_, _| true,
149 )?;
150
151 {
154 let mut previous_i = syriac_glyphs
155 .iter()
156 .position(|g| !g.is_transparent())
157 .unwrap_or(0);
158
159 for i in (previous_i + 1)..syriac_glyphs.len() {
160 if syriac_glyphs[i].is_transparent() {
161 continue;
162 }
163
164 if syriac_glyphs[previous_i].is_left_joining() && syriac_glyphs[i].is_right_joining() {
165 if syriac_glyphs[i].is_alaph() {
166 syriac_glyphs[i].set_feature_tag(tag::MED2)
167 } else {
168 syriac_glyphs[i].set_feature_tag(tag::FINA)
169 };
170
171 match syriac_glyphs[previous_i].feature_tag() {
172 tag::ISOL => syriac_glyphs[previous_i].set_feature_tag(tag::INIT),
173 tag::FINA => syriac_glyphs[previous_i].set_feature_tag(tag::MEDI),
174 _ => {}
175 }
176 }
177
178 previous_i = i;
179 }
180
181 let last_i = syriac_glyphs
182 .iter()
183 .rposition(|g| !(g.is_transparent() || g.is_non_joining()))
184 .unwrap_or(0);
185
186 if last_i != 0 && syriac_glyphs[last_i].is_alaph() {
187 let previous_i = last_i - 1;
188
189 if syriac_glyphs[previous_i].is_left_joining() {
190 syriac_glyphs[last_i].set_feature_tag(tag::FINA)
191 } else if syriac_glyphs[previous_i].is_dalath_rish() {
192 syriac_glyphs[last_i].set_feature_tag(tag::FIN3)
193 } else {
194 syriac_glyphs[last_i].set_feature_tag(tag::FIN2)
195 }
196 }
197 }
198
199 const LANGUAGE_FEATURES: &[(FeatureMask, bool)] = &[
206 (FeatureMask::LOCL, true),
207 (FeatureMask::ISOL, false),
208 (FeatureMask::FINA, false),
209 (FeatureMask::FIN2, false),
210 (FeatureMask::FIN3, false),
211 (FeatureMask::MEDI, false),
212 (FeatureMask::MED2, false),
213 (FeatureMask::INIT, false),
214 (FeatureMask::RLIG, true),
215 (FeatureMask::CALT, true),
216 ];
217
218 for &(feature_mask, is_global) in LANGUAGE_FEATURES {
219 apply_lookups(
220 feature_mask,
221 gsub_cache,
222 gsub_table,
223 gdef_table,
224 script_tag,
225 lang_tag,
226 feature_variations,
227 syriac_glyphs,
228 |g, feature_tag| is_global || g.feature_tag() == feature_tag,
229 )?;
230 }
231
232 apply_lookups(
237 FeatureMask::LIGA,
238 gsub_cache,
239 gsub_table,
240 gdef_table,
241 script_tag,
242 lang_tag,
243 feature_variations,
244 syriac_glyphs,
245 |_, _| true,
246 )?;
247
248 *raw_glyphs = syriac_glyphs.iter().map(RawGlyph::from).collect();
253
254 Ok(())
255}
256
257fn apply_lookups(
258 feature_mask: FeatureMask,
259 gsub_cache: &LayoutCache<GSUB>,
260 gsub_table: &LayoutTable<GSUB>,
261 gdef_table: Option<&GDEFTable>,
262 script_tag: u32,
263 lang_tag: Option<u32>,
264 feature_variations: Option<&FeatureTableSubstitution<'_>>,
265 syriac_glyphs: &mut Vec<RawGlyph<SyriacData>>,
266 pred: impl Fn(&RawGlyph<SyriacData>, u32) -> bool + Copy,
267) -> Result<(), ParseError> {
268 let index = gsub::get_lookups_cache_index(
269 gsub_cache,
270 script_tag,
271 lang_tag,
272 feature_variations,
273 feature_mask,
274 )?;
275 let lookups = &gsub_cache.cached_lookups.borrow()[index];
276
277 for &(lookup_index, feature_tag) in lookups {
278 gsub::gsub_apply_lookup(
279 gsub_cache,
280 gsub_table,
281 gdef_table,
282 lookup_index,
283 feature_tag,
284 None,
285 syriac_glyphs,
286 0,
287 syriac_glyphs.len(),
288 |g| pred(g, feature_tag),
289 )?;
290 }
291
292 Ok(())
293}