1 | //! OpenType layout. |
2 | |
3 | use core::ops::{Index, IndexMut}; |
4 | |
5 | use super::buffer::*; |
6 | use super::common::TagExt; |
7 | use super::ot_layout_gsubgpos::{Apply, OT}; |
8 | use super::ot_shape_plan::hb_ot_shape_plan_t; |
9 | use super::unicode::{hb_unicode_funcs_t, hb_unicode_general_category_t, GeneralCategoryExt}; |
10 | use super::{hb_font_t, hb_glyph_info_t, hb_tag_t}; |
11 | use crate::hb::set_digest::{hb_set_digest_ext, hb_set_digest_t}; |
12 | use ttf_parser::opentype_layout::{FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex}; |
13 | |
14 | pub const MAX_NESTING_LEVEL: usize = 64; |
15 | pub const MAX_CONTEXT_LENGTH: usize = 64; |
16 | |
17 | pub fn hb_ot_layout_has_kerning(face: &hb_font_t) -> bool { |
18 | face.tables().kern.is_some() |
19 | } |
20 | |
21 | pub fn hb_ot_layout_has_machine_kerning(face: &hb_font_t) -> bool { |
22 | match face.tables().kern { |
23 | Some(ref kern: &Table<'_>) => kern.subtables.into_iter().any(|s: Subtable<'_>| s.has_state_machine), |
24 | None => false, |
25 | } |
26 | } |
27 | |
28 | pub fn hb_ot_layout_has_cross_kerning(face: &hb_font_t) -> bool { |
29 | match face.tables().kern { |
30 | Some(ref kern: &Table<'_>) => kern.subtables.into_iter().any(|s: Subtable<'_>| s.has_cross_stream), |
31 | None => false, |
32 | } |
33 | } |
34 | |
35 | // hb_ot_layout_kern |
36 | |
37 | // OT::GDEF::is_blocklisted unsupported |
38 | |
39 | pub fn _hb_ot_layout_set_glyph_props(face: &hb_font_t, buffer: &mut hb_buffer_t) { |
40 | let len: usize = buffer.len; |
41 | for info: &mut hb_glyph_info_t in &mut buffer.info[..len] { |
42 | info.set_glyph_props(face.glyph_props(info.as_glyph())); |
43 | info.set_lig_props(0); |
44 | } |
45 | } |
46 | |
47 | pub fn hb_ot_layout_has_glyph_classes(face: &hb_font_t) -> bool { |
48 | face.tables() |
49 | .gdef |
50 | .map_or(default:false, |table: Table<'_>| table.has_glyph_classes()) |
51 | } |
52 | |
53 | // get_gsubgpos_table |
54 | |
55 | #[derive (Clone, Copy, Debug, PartialEq, Eq)] |
56 | pub enum TableIndex { |
57 | GSUB = 0, |
58 | GPOS = 1, |
59 | } |
60 | |
61 | impl TableIndex { |
62 | pub fn iter() -> impl Iterator<Item = TableIndex> { |
63 | [Self::GSUB, Self::GPOS].iter().copied() |
64 | } |
65 | } |
66 | |
67 | impl<T> Index<TableIndex> for [T] { |
68 | type Output = T; |
69 | |
70 | fn index(&self, table_index: TableIndex) -> &Self::Output { |
71 | &self[table_index as usize] |
72 | } |
73 | } |
74 | |
75 | impl<T> IndexMut<TableIndex> for [T] { |
76 | fn index_mut(&mut self, table_index: TableIndex) -> &mut Self::Output { |
77 | &mut self[table_index as usize] |
78 | } |
79 | } |
80 | |
81 | /// A lookup-based layout table (GSUB or GPOS). |
82 | pub trait LayoutTable { |
83 | /// The index of this table. |
84 | const INDEX: TableIndex; |
85 | |
86 | /// Whether lookups in this table can be applied to the buffer in-place. |
87 | const IN_PLACE: bool; |
88 | |
89 | /// The kind of lookup stored in this table. |
90 | type Lookup: LayoutLookup; |
91 | |
92 | /// Get the lookup at the specified index. |
93 | fn get_lookup(&self, index: LookupIndex) -> Option<&Self::Lookup>; |
94 | } |
95 | |
96 | /// A lookup in a layout table. |
97 | pub trait LayoutLookup: Apply { |
98 | /// The lookup's lookup_props. |
99 | fn props(&self) -> u32; |
100 | |
101 | /// Whether the lookup has to be applied backwards. |
102 | fn is_reverse(&self) -> bool; |
103 | |
104 | /// The digest of the lookup. |
105 | fn digest(&self) -> &hb_set_digest_t; |
106 | } |
107 | |
108 | pub trait LayoutTableExt { |
109 | fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)>; |
110 | fn select_script_language( |
111 | &self, |
112 | script_index: ScriptIndex, |
113 | lang_tags: &[hb_tag_t], |
114 | ) -> Option<LanguageIndex>; |
115 | fn get_required_language_feature( |
116 | &self, |
117 | script_index: ScriptIndex, |
118 | lang_index: Option<LanguageIndex>, |
119 | ) -> Option<(FeatureIndex, hb_tag_t)>; |
120 | fn find_language_feature( |
121 | &self, |
122 | script_index: ScriptIndex, |
123 | lang_index: Option<LanguageIndex>, |
124 | feature_tag: hb_tag_t, |
125 | ) -> Option<FeatureIndex>; |
126 | } |
127 | |
128 | impl LayoutTableExt for ttf_parser::opentype_layout::LayoutTable<'_> { |
129 | // hb_ot_layout_table_select_script |
130 | /// Returns true + index and tag of the first found script tag in the given GSUB or GPOS table |
131 | /// or false + index and tag if falling back to a default script. |
132 | fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)> { |
133 | for &tag in script_tags { |
134 | if let Some(index) = self.scripts.index(tag) { |
135 | return Some((true, index, tag)); |
136 | } |
137 | } |
138 | |
139 | for &tag in &[ |
140 | // try finding 'DFLT' |
141 | hb_tag_t::default_script(), |
142 | // try with 'dflt'; MS site has had typos and many fonts use it now :( |
143 | hb_tag_t::default_language(), |
144 | // try with 'latn'; some old fonts put their features there even though |
145 | // they're really trying to support Thai, for example :( |
146 | hb_tag_t::from_bytes(b"latn" ), |
147 | ] { |
148 | if let Some(index) = self.scripts.index(tag) { |
149 | return Some((false, index, tag)); |
150 | } |
151 | } |
152 | |
153 | None |
154 | } |
155 | |
156 | // hb_ot_layout_script_select_language |
157 | /// Returns the index of the first found language tag in the given GSUB or GPOS table, |
158 | /// underneath the specified script index. |
159 | fn select_script_language( |
160 | &self, |
161 | script_index: ScriptIndex, |
162 | lang_tags: &[hb_tag_t], |
163 | ) -> Option<LanguageIndex> { |
164 | let script = self.scripts.get(script_index)?; |
165 | |
166 | for &tag in lang_tags { |
167 | if let Some(index) = script.languages.index(tag) { |
168 | return Some(index); |
169 | } |
170 | } |
171 | |
172 | // try finding 'dflt' |
173 | if let Some(index) = script.languages.index(hb_tag_t::default_language()) { |
174 | return Some(index); |
175 | } |
176 | |
177 | None |
178 | } |
179 | |
180 | // hb_ot_layout_language_get_required_feature |
181 | /// Returns the index and tag of a required feature in the given GSUB or GPOS table, |
182 | /// underneath the specified script and language. |
183 | fn get_required_language_feature( |
184 | &self, |
185 | script_index: ScriptIndex, |
186 | lang_index: Option<LanguageIndex>, |
187 | ) -> Option<(FeatureIndex, hb_tag_t)> { |
188 | let script = self.scripts.get(script_index)?; |
189 | let sys = match lang_index { |
190 | Some(index) => script.languages.get(index)?, |
191 | None => script.default_language?, |
192 | }; |
193 | let idx = sys.required_feature?; |
194 | let tag = self.features.get(idx)?.tag; |
195 | Some((idx, tag)) |
196 | } |
197 | |
198 | // hb_ot_layout_language_find_feature |
199 | /// Returns the index of a given feature tag in the given GSUB or GPOS table, |
200 | /// underneath the specified script and language. |
201 | fn find_language_feature( |
202 | &self, |
203 | script_index: ScriptIndex, |
204 | lang_index: Option<LanguageIndex>, |
205 | feature_tag: hb_tag_t, |
206 | ) -> Option<FeatureIndex> { |
207 | let script = self.scripts.get(script_index)?; |
208 | let sys = match lang_index { |
209 | Some(index) => script.languages.get(index)?, |
210 | None => script.default_language?, |
211 | }; |
212 | |
213 | for i in 0..sys.feature_indices.len() { |
214 | if let Some(index) = sys.feature_indices.get(i) { |
215 | if self.features.get(index).map(|v| v.tag) == Some(feature_tag) { |
216 | return Some(index); |
217 | } |
218 | } |
219 | } |
220 | |
221 | None |
222 | } |
223 | } |
224 | |
225 | /// Called before substitution lookups are performed, to ensure that glyph |
226 | /// class and other properties are set on the glyphs in the buffer. |
227 | pub fn hb_ot_layout_substitute_start(face: &hb_font_t, buffer: &mut hb_buffer_t) { |
228 | _hb_ot_layout_set_glyph_props(face, buffer) |
229 | } |
230 | |
231 | /// Applies the lookups in the given GSUB or GPOS table. |
232 | pub fn apply_layout_table<T: LayoutTable>( |
233 | plan: &hb_ot_shape_plan_t, |
234 | face: &hb_font_t, |
235 | buffer: &mut hb_buffer_t, |
236 | table: Option<&T>, |
237 | ) { |
238 | let mut ctx = OT::hb_ot_apply_context_t::new(T::INDEX, face, buffer); |
239 | |
240 | for (stage_index, stage) in plan.ot_map.stages(T::INDEX).iter().enumerate() { |
241 | if let Some(table) = table { |
242 | for lookup_map in plan.ot_map.stage_lookups(T::INDEX, stage_index) { |
243 | let Some(lookup) = table.get_lookup(lookup_map.index) else { |
244 | continue; |
245 | }; |
246 | |
247 | if lookup.digest().may_have(&ctx.digest) { |
248 | ctx.lookup_index = lookup_map.index; |
249 | ctx.set_lookup_mask(lookup_map.mask); |
250 | ctx.auto_zwj = lookup_map.auto_zwj; |
251 | ctx.auto_zwnj = lookup_map.auto_zwnj; |
252 | |
253 | ctx.random = lookup_map.random; |
254 | ctx.per_syllable = lookup_map.per_syllable; |
255 | |
256 | apply_string::<T>(&mut ctx, lookup); |
257 | } |
258 | } |
259 | } |
260 | |
261 | if let Some(func) = stage.pause_func { |
262 | if func(plan, face, ctx.buffer) { |
263 | ctx.digest = ctx.buffer.digest(); |
264 | } |
265 | } |
266 | } |
267 | } |
268 | |
269 | fn apply_string<T: LayoutTable>(ctx: &mut OT::hb_ot_apply_context_t, lookup: &T::Lookup) { |
270 | if ctx.buffer.is_empty() || ctx.lookup_mask() == 0 { |
271 | return; |
272 | } |
273 | |
274 | ctx.lookup_props = lookup.props(); |
275 | |
276 | if !lookup.is_reverse() { |
277 | // in/out forward substitution/positioning |
278 | if !T::IN_PLACE { |
279 | ctx.buffer.clear_output(); |
280 | } |
281 | ctx.buffer.idx = 0; |
282 | apply_forward(ctx, lookup); |
283 | |
284 | if !T::IN_PLACE { |
285 | ctx.buffer.sync(); |
286 | } |
287 | } else { |
288 | // in-place backward substitution/positioning |
289 | assert!(!ctx.buffer.have_output); |
290 | |
291 | ctx.buffer.idx = ctx.buffer.len - 1; |
292 | apply_backward(ctx, lookup); |
293 | } |
294 | } |
295 | |
296 | fn apply_forward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool { |
297 | let mut ret: bool = false; |
298 | while ctx.buffer.idx < ctx.buffer.len && ctx.buffer.successful { |
299 | let cur: &hb_glyph_info_t = ctx.buffer.cur(0); |
300 | if (cur.mask & ctx.lookup_mask()) != 0 |
301 | && ctx.check_glyph_property(info:cur, match_props:ctx.lookup_props) |
302 | && lookup.apply(ctx).is_some() |
303 | { |
304 | ret = true; |
305 | } else { |
306 | ctx.buffer.next_glyph(); |
307 | } |
308 | } |
309 | ret |
310 | } |
311 | |
312 | fn apply_backward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool { |
313 | let mut ret: bool = false; |
314 | loop { |
315 | let cur: &hb_glyph_info_t = ctx.buffer.cur(0); |
316 | ret |= (cur.mask & ctx.lookup_mask()) != 0 |
317 | && ctx.check_glyph_property(info:cur, match_props:ctx.lookup_props) |
318 | && lookup.apply(ctx).is_some(); |
319 | |
320 | if ctx.buffer.idx == 0 { |
321 | break; |
322 | } |
323 | |
324 | ctx.buffer.idx -= 1; |
325 | } |
326 | ret |
327 | } |
328 | |
329 | /* unicode_props */ |
330 | |
331 | /* Design: |
332 | * unicode_props() is a two-byte number. The low byte includes: |
333 | * - Modified General_Category: 5 bits. |
334 | * - A bit each for: |
335 | * * Is it Default_Ignorable(); we have a modified Default_Ignorable(). |
336 | * * Whether it's one of the four Mongolian Free Variation Selectors, |
337 | * CGJ, or other characters that are hidden but should not be ignored |
338 | * like most other Default_Ignorable()s do during GSUB matching. |
339 | * * Whether it's a grapheme continuation. |
340 | * |
341 | * The high-byte has different meanings, switched by the Gen-Cat: |
342 | * - For Mn,Mc,Me: the modified Combining_Class. |
343 | * - For Cf: whether it's ZWJ, ZWNJ, or something else. |
344 | * - For Ws: index of which space character this is, if space fallback |
345 | * is needed, ie. we don't set this by default, only if asked to. |
346 | * |
347 | * Above I said "modified" General_Category. This is because we need to |
348 | * remember Variation Selectors, and we don't have bits left. So we |
349 | * change their Gen_Cat from Mn to Cf, and use a bit of the high byte to |
350 | * remember them. |
351 | */ |
352 | |
353 | // enum hb_unicode_props_flags_t { |
354 | // UPROPS_MASK_GEN_CAT = 0x001Fu, |
355 | // UPROPS_MASK_IGNORABLE = 0x0020u, |
356 | // UPROPS_MASK_HIDDEN = 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..4, or TAG characters */ |
357 | // UPROPS_MASK_CONTINUATION=0x0080u, |
358 | |
359 | // /* If GEN_CAT=FORMAT, top byte masks: */ |
360 | // UPROPS_MASK_Cf_ZWJ = 0x0100u, |
361 | // UPROPS_MASK_Cf_ZWNJ = 0x0200u |
362 | // }; |
363 | // HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t); |
364 | |
365 | // static inline void |
366 | // _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) |
367 | // { |
368 | // hb_unicode_funcs_t *unicode = buffer->unicode; |
369 | // unsigned int u = info->codepoint; |
370 | // unsigned int gen_cat = (unsigned int) unicode->general_category (u); |
371 | // unsigned int props = gen_cat; |
372 | |
373 | // if (u >= 0x80u) |
374 | // { |
375 | // buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII; |
376 | |
377 | // if (unlikely (unicode->is_default_ignorable (u))) |
378 | // { |
379 | // buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES; |
380 | // props |= UPROPS_MASK_IGNORABLE; |
381 | // if (u == 0x200Cu) props |= UPROPS_MASK_Cf_ZWNJ; |
382 | // else if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ; |
383 | // /* Mongolian Free Variation Selectors need to be remembered |
384 | // * because although we need to hide them like default-ignorables, |
385 | // * they need to non-ignorable during shaping. This is similar to |
386 | // * what we do for joiners in Indic-like shapers, but since the |
387 | // * FVSes are GC=Mn, we have use a separate bit to remember them. |
388 | // * Fixes: |
389 | // * https://github.com/harfbuzz/harfbuzz/issues/234 */ |
390 | // else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x180Bu, 0x180Du, 0x180Fu, 0x180Fu))) props |= UPROPS_MASK_HIDDEN; |
391 | // /* TAG characters need similar treatment. Fixes: |
392 | // * https://github.com/harfbuzz/harfbuzz/issues/463 */ |
393 | // else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN; |
394 | // /* COMBINING GRAPHEME JOINER should not be skipped; at least some times. |
395 | // * https://github.com/harfbuzz/harfbuzz/issues/554 */ |
396 | // else if (unlikely (u == 0x034Fu)) |
397 | // { |
398 | // buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CGJ; |
399 | // props |= UPROPS_MASK_HIDDEN; |
400 | // } |
401 | // } |
402 | |
403 | // if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat))) |
404 | // { |
405 | // props |= UPROPS_MASK_CONTINUATION; |
406 | // props |= unicode->modified_combining_class (u)<<8; |
407 | // } |
408 | // } |
409 | |
410 | // info->unicode_props() = props; |
411 | // } |
412 | |
413 | #[inline ] |
414 | pub fn _hb_glyph_info_set_general_category( |
415 | info: &mut hb_glyph_info_t, |
416 | gen_cat: hb_unicode_general_category_t, |
417 | ) { |
418 | /* Clears top-byte. */ |
419 | let gen_cat: u32 = gen_cat.to_rb(); |
420 | let n: u16 = |
421 | (gen_cat as u16) | (info.unicode_props() & (0xFF & !UnicodeProps::GENERAL_CATEGORY.bits())); |
422 | info.set_unicode_props(n); |
423 | } |
424 | |
425 | #[inline ] |
426 | pub fn _hb_glyph_info_get_general_category( |
427 | info: &hb_glyph_info_t, |
428 | ) -> hb_unicode_general_category_t { |
429 | let n: u16 = info.unicode_props() & UnicodeProps::GENERAL_CATEGORY.bits(); |
430 | hb_unicode_general_category_t::from_rb(gc:n as u32) |
431 | } |
432 | |
433 | #[inline ] |
434 | pub fn _hb_glyph_info_is_unicode_mark(info: &hb_glyph_info_t) -> bool { |
435 | _hb_glyph_info_get_general_category(info).is_mark() |
436 | } |
437 | |
438 | #[inline ] |
439 | pub(crate) fn _hb_glyph_info_set_modified_combining_class( |
440 | info: &mut hb_glyph_info_t, |
441 | modified_class: u8, |
442 | ) { |
443 | if !_hb_glyph_info_is_unicode_mark(info) { |
444 | return; |
445 | } |
446 | |
447 | let n: u16 = ((modified_class as u16) << 8) | (info.unicode_props() & 0xFF); |
448 | info.set_unicode_props(n); |
449 | } |
450 | |
451 | #[inline ] |
452 | pub fn _hb_glyph_info_get_modified_combining_class(info: &hb_glyph_info_t) -> u8 { |
453 | if _hb_glyph_info_is_unicode_mark(info) { |
454 | (info.unicode_props() >> 8) as u8 |
455 | } else { |
456 | 0 |
457 | } |
458 | } |
459 | |
460 | // TODO: use |
461 | // #[inline] |
462 | // pub fn info_cc(info: &hb_glyph_info_t) -> u8 { |
463 | // _hb_glyph_info_get_modified_combining_class(info) |
464 | // } |
465 | |
466 | #[inline ] |
467 | pub(crate) fn _hb_glyph_info_is_unicode_space(info: &hb_glyph_info_t) -> bool { |
468 | _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::SpaceSeparator |
469 | } |
470 | |
471 | #[inline ] |
472 | pub(crate) fn _hb_glyph_info_set_unicode_space_fallback_type( |
473 | info: &mut hb_glyph_info_t, |
474 | s: hb_unicode_funcs_t::space_t, |
475 | ) { |
476 | if !_hb_glyph_info_is_unicode_space(info) { |
477 | return; |
478 | } |
479 | |
480 | let n: u16 = ((s as u16) << 8) | (info.unicode_props() & 0xFF); |
481 | info.set_unicode_props(n); |
482 | } |
483 | |
484 | #[inline ] |
485 | pub(crate) fn _hb_glyph_info_get_unicode_space_fallback_type( |
486 | info: &hb_glyph_info_t, |
487 | ) -> hb_unicode_funcs_t::space_t { |
488 | if _hb_glyph_info_is_unicode_space(info) { |
489 | (info.unicode_props() >> 8) as u8 |
490 | } else { |
491 | hb_unicode_funcs_t::NOT_SPACE |
492 | } |
493 | } |
494 | |
495 | #[inline ] |
496 | pub(crate) fn _hb_glyph_info_is_variation_selector(info: &hb_glyph_info_t) -> bool { |
497 | let a: bool = _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::Format; |
498 | let b: bool = (info.unicode_props() & UnicodeProps::CF_VS.bits()) != 0; |
499 | a && b |
500 | } |
501 | |
502 | #[inline ] |
503 | pub(crate) fn _hb_glyph_info_set_variation_selector(info: &mut hb_glyph_info_t, customize: bool) { |
504 | if customize { |
505 | _hb_glyph_info_set_general_category(info, gen_cat:hb_unicode_general_category_t::Format); |
506 | info.set_unicode_props(info.unicode_props() | UnicodeProps::CF_VS.bits()) |
507 | } else { |
508 | // Reset to their original condition |
509 | _hb_glyph_info_set_general_category(info, gen_cat:hb_unicode_general_category_t::NonspacingMark); |
510 | } |
511 | } |
512 | |
513 | #[inline ] |
514 | pub(crate) fn _hb_glyph_info_is_default_ignorable(info: &hb_glyph_info_t) -> bool { |
515 | let n: u16 = info.unicode_props() & UnicodeProps::IGNORABLE.bits(); |
516 | n != 0 && !_hb_glyph_info_substituted(info) |
517 | } |
518 | |
519 | #[inline ] |
520 | pub(crate) fn _hb_glyph_info_clear_default_ignorable(info: &mut hb_glyph_info_t) { |
521 | let mut n: u16 = info.unicode_props(); |
522 | n &= !UnicodeProps::IGNORABLE.bits(); |
523 | info.set_unicode_props(n); |
524 | } |
525 | |
526 | #[inline ] |
527 | pub(crate) fn _hb_glyph_info_is_hidden(info: &hb_glyph_info_t) -> bool { |
528 | (info.unicode_props() & UnicodeProps::HIDDEN.bits()) != 0 |
529 | } |
530 | |
531 | // static inline void |
532 | // _hb_glyph_info_unhide (hb_glyph_info_t *info) |
533 | // { |
534 | // info->unicode_props() &= ~ UPROPS_MASK_HIDDEN; |
535 | // } |
536 | |
537 | #[inline ] |
538 | pub(crate) fn _hb_glyph_info_set_continuation(info: &mut hb_glyph_info_t) { |
539 | let mut n: u16 = info.unicode_props(); |
540 | n |= UnicodeProps::CONTINUATION.bits(); |
541 | info.set_unicode_props(n); |
542 | } |
543 | |
544 | #[inline ] |
545 | pub(crate) fn _hb_glyph_info_reset_continuation(info: &mut hb_glyph_info_t) { |
546 | let mut n: u16 = info.unicode_props(); |
547 | n &= !UnicodeProps::CONTINUATION.bits(); |
548 | info.set_unicode_props(n); |
549 | } |
550 | |
551 | #[inline ] |
552 | pub(crate) fn _hb_glyph_info_is_continuation(info: &hb_glyph_info_t) -> bool { |
553 | info.unicode_props() & UnicodeProps::CONTINUATION.bits() != 0 |
554 | } |
555 | |
556 | pub(crate) fn _hb_grapheme_group_func(_: &hb_glyph_info_t, b: &hb_glyph_info_t) -> bool { |
557 | _hb_glyph_info_is_continuation(info:b) |
558 | } |
559 | |
560 | pub fn _hb_ot_layout_reverse_graphemes(buffer: &mut hb_buffer_t) { |
561 | buffer.reverse_groups( |
562 | group:_hb_grapheme_group_func, |
563 | merge_clusters:buffer.cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, |
564 | ) |
565 | } |
566 | |
567 | #[inline ] |
568 | pub(crate) fn _hb_glyph_info_is_unicode_format(info: &hb_glyph_info_t) -> bool { |
569 | _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::Format |
570 | } |
571 | |
572 | #[inline ] |
573 | pub(crate) fn _hb_glyph_info_is_zwnj(info: &hb_glyph_info_t) -> bool { |
574 | _hb_glyph_info_is_unicode_format(info) |
575 | && (info.unicode_props() & UnicodeProps::CF_ZWNJ.bits() != 0) |
576 | } |
577 | |
578 | #[inline ] |
579 | pub(crate) fn _hb_glyph_info_is_zwj(info: &hb_glyph_info_t) -> bool { |
580 | _hb_glyph_info_is_unicode_format(info) |
581 | && (info.unicode_props() & UnicodeProps::CF_ZWJ.bits() != 0) |
582 | } |
583 | |
584 | // static inline bool |
585 | // _hb_glyph_info_is_joiner (const hb_glyph_info_t *info) |
586 | // { |
587 | // return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ)); |
588 | // } |
589 | |
590 | // static inline void |
591 | // _hb_glyph_info_flip_joiners (hb_glyph_info_t *info) |
592 | // { |
593 | // if (!_hb_glyph_info_is_unicode_format (info)) |
594 | // return; |
595 | // info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ; |
596 | // } |
597 | |
598 | // /* lig_props: aka lig_id / lig_comp |
599 | // * |
600 | // * When a ligature is formed: |
601 | // * |
602 | // * - The ligature glyph and any marks in between all the same newly allocated |
603 | // * lig_id, |
604 | // * - The ligature glyph will get lig_num_comps set to the number of components |
605 | // * - The marks get lig_comp > 0, reflecting which component of the ligature |
606 | // * they were applied to. |
607 | // * - This is used in GPOS to attach marks to the right component of a ligature |
608 | // * in MarkLigPos, |
609 | // * - Note that when marks are ligated together, much of the above is skipped |
610 | // * and the current lig_id reused. |
611 | // * |
612 | // * When a multiple-substitution is done: |
613 | // * |
614 | // * - All resulting glyphs will have lig_id = 0, |
615 | // * - The resulting glyphs will have lig_comp = 0, 1, 2, ... respectively. |
616 | // * - This is used in GPOS to attach marks to the first component of a |
617 | // * multiple substitution in MarkBasePos. |
618 | // * |
619 | // * The numbers are also used in GPOS to do mark-to-mark positioning only |
620 | // * to marks that belong to the same component of the same ligature. |
621 | // */ |
622 | // static inline void |
623 | // _hb_glyph_info_clear_lig_props (hb_glyph_info_t *info) |
624 | // { |
625 | // info->lig_props() = 0; |
626 | // } |
627 | |
628 | const IS_LIG_BASE: u8 = 0x10; |
629 | |
630 | #[inline ] |
631 | pub(crate) fn _hb_glyph_info_set_lig_props_for_ligature( |
632 | info: &mut hb_glyph_info_t, |
633 | lig_id: u8, |
634 | lig_num_comps: u8, |
635 | ) { |
636 | info.set_lig_props((lig_id << 5) | IS_LIG_BASE | (lig_num_comps & 0x0F)); |
637 | } |
638 | |
639 | #[inline ] |
640 | pub(crate) fn _hb_glyph_info_set_lig_props_for_mark( |
641 | info: &mut hb_glyph_info_t, |
642 | lig_id: u8, |
643 | lig_comp: u8, |
644 | ) { |
645 | info.set_lig_props((lig_id << 5) | (lig_comp & 0x0F)); |
646 | } |
647 | |
648 | #[inline ] |
649 | pub(crate) fn _hb_glyph_info_set_lig_props_for_component(info: &mut hb_glyph_info_t, comp: u8) { |
650 | _hb_glyph_info_set_lig_props_for_mark(info, lig_id:0, comp); |
651 | } |
652 | |
653 | #[inline ] |
654 | pub(crate) fn _hb_glyph_info_get_lig_id(info: &hb_glyph_info_t) -> u8 { |
655 | info.lig_props() >> 5 |
656 | } |
657 | |
658 | #[inline ] |
659 | pub(crate) fn _hb_glyph_info_ligated_internal(info: &hb_glyph_info_t) -> bool { |
660 | info.lig_props() & IS_LIG_BASE != 0 |
661 | } |
662 | |
663 | #[inline ] |
664 | pub(crate) fn _hb_glyph_info_get_lig_comp(info: &hb_glyph_info_t) -> u8 { |
665 | if _hb_glyph_info_ligated_internal(info) { |
666 | 0 |
667 | } else { |
668 | info.lig_props() & 0x0F |
669 | } |
670 | } |
671 | |
672 | #[inline ] |
673 | pub(crate) fn _hb_glyph_info_get_lig_num_comps(info: &hb_glyph_info_t) -> u8 { |
674 | if info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0 |
675 | && _hb_glyph_info_ligated_internal(info) |
676 | { |
677 | info.lig_props() & 0x0F |
678 | } else { |
679 | 1 |
680 | } |
681 | } |
682 | |
683 | // /* glyph_props: */ |
684 | // static inline void |
685 | // _hb_glyph_info_set_glyph_props (hb_glyph_info_t *info, unsigned int props) |
686 | // { |
687 | // info->glyph_props() = props; |
688 | // } |
689 | |
690 | // static inline unsigned int |
691 | // _hb_glyph_info_get_glyph_props (const hb_glyph_info_t *info) |
692 | // { |
693 | // return info->glyph_props(); |
694 | // } |
695 | |
696 | #[inline ] |
697 | pub(crate) fn _hb_glyph_info_is_base_glyph(info: &hb_glyph_info_t) -> bool { |
698 | info.glyph_props() & GlyphPropsFlags::BASE_GLYPH.bits() != 0 |
699 | } |
700 | |
701 | #[inline ] |
702 | pub(crate) fn _hb_glyph_info_is_ligature(info: &hb_glyph_info_t) -> bool { |
703 | info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0 |
704 | } |
705 | |
706 | #[inline ] |
707 | pub(crate) fn _hb_glyph_info_is_mark(info: &hb_glyph_info_t) -> bool { |
708 | info.glyph_props() & GlyphPropsFlags::MARK.bits() != 0 |
709 | } |
710 | |
711 | #[inline ] |
712 | pub(crate) fn _hb_glyph_info_substituted(info: &hb_glyph_info_t) -> bool { |
713 | info.glyph_props() & GlyphPropsFlags::SUBSTITUTED.bits() != 0 |
714 | } |
715 | |
716 | #[inline ] |
717 | pub(crate) fn _hb_glyph_info_ligated(info: &hb_glyph_info_t) -> bool { |
718 | info.glyph_props() & GlyphPropsFlags::LIGATED.bits() != 0 |
719 | } |
720 | |
721 | #[inline ] |
722 | pub(crate) fn _hb_glyph_info_multiplied(info: &hb_glyph_info_t) -> bool { |
723 | info.glyph_props() & GlyphPropsFlags::MULTIPLIED.bits() != 0 |
724 | } |
725 | |
726 | #[inline ] |
727 | pub(crate) fn _hb_glyph_info_ligated_and_didnt_multiply(info: &hb_glyph_info_t) -> bool { |
728 | _hb_glyph_info_ligated(info) && !_hb_glyph_info_multiplied(info) |
729 | } |
730 | |
731 | #[inline ] |
732 | pub(crate) fn _hb_glyph_info_clear_ligated_and_multiplied(info: &mut hb_glyph_info_t) { |
733 | let mut n: u16 = info.glyph_props(); |
734 | n &= !(GlyphPropsFlags::LIGATED | GlyphPropsFlags::MULTIPLIED).bits(); |
735 | info.set_glyph_props(n); |
736 | } |
737 | |
738 | #[inline ] |
739 | pub(crate) fn _hb_glyph_info_clear_substituted(info: &mut hb_glyph_info_t) { |
740 | let mut n: u16 = info.glyph_props(); |
741 | n &= !GlyphPropsFlags::SUBSTITUTED.bits(); |
742 | info.set_glyph_props(n); |
743 | } |
744 | |
745 | pub fn _hb_clear_substitution_flags( |
746 | _: &hb_ot_shape_plan_t, |
747 | _: &hb_font_t, |
748 | buffer: &mut hb_buffer_t, |
749 | ) -> bool { |
750 | let len: usize = buffer.len; |
751 | for info: &mut hb_glyph_info_t in &mut buffer.info[..len] { |
752 | _hb_glyph_info_clear_substituted(info); |
753 | } |
754 | |
755 | false |
756 | } |
757 | |