| 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 | |