diff --git a/src/hb/fonta/ot/contextual.rs b/src/hb/fonta/ot/contextual.rs index e06c39e..48f786e 100644 --- a/src/hb/fonta/ot/contextual.rs +++ b/src/hb/fonta/ot/contextual.rs @@ -1,3 +1,4 @@ +use super::{coverage_index, covered, glyph_class}; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; use crate::hb::ot_layout_gsubgpos::{ apply_lookup, match_backtrack, match_func_t, match_glyph, match_input, match_lookahead, Apply, @@ -5,13 +6,185 @@ use crate::hb::ot_layout_gsubgpos::{ }; use skrifa::raw::tables::layout::{ ChainedSequenceContextFormat1, ChainedSequenceContextFormat2, ChainedSequenceContextFormat3, + SequenceContextFormat1, SequenceContextFormat2, SequenceContextFormat3, SequenceLookupRecord, }; use skrifa::raw::types::BigEndian; -use ttf_parser::{opentype_layout::SequenceLookupRecord, GlyphId}; +use ttf_parser::GlyphId; + +impl WouldApply for SequenceContextFormat1<'_> { + fn would_apply(&self, ctx: &WouldApplyContext) -> bool { + coverage_index(self.coverage(), ctx.glyphs[0]) + .and_then(|index| { + self.seq_rule_sets() + .get(index as usize) + .transpose() + .ok() + .flatten() + }) + .map(|set| { + set.seq_rules().iter().any(|rule| { + rule.map(|rule| { + let input = rule.input_sequence(); + ctx.glyphs.len() == input.len() + 1 + && input.iter().enumerate().all(|(i, value)| { + match_glyph(ctx.glyphs[i + 1], value.get().to_u16()) + }) + }) + .unwrap_or(false) + }) + }) + .unwrap_or_default() + } +} + +impl Apply for SequenceContextFormat1<'_> { + fn apply( + &self, + ctx: &mut crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t, + ) -> Option<()> { + let glyph = skrifa::GlyphId::from(ctx.buffer.cur(0).as_glyph().0); + let index = self.coverage().ok()?.get(glyph)? as usize; + let set = self.seq_rule_sets().get(index)?.ok()?; + for rule in set.seq_rules().iter().filter_map(|rule| rule.ok()) { + let input = rule.input_sequence(); + if apply_context(ctx, input, &match_glyph, rule.seq_lookup_records()).is_some() { + return Some(()); + } + } + None + } +} + +impl WouldApply for SequenceContextFormat2<'_> { + fn would_apply(&self, ctx: &WouldApplyContext) -> bool { + let class_def = self.class_def().ok(); + let match_fn = &match_class(&class_def); + let class = glyph_class(self.class_def(), ctx.glyphs[0]); + self.class_seq_rule_sets() + .get(class as usize) + .transpose() + .ok() + .flatten() + .map(|set| { + set.class_seq_rules().iter().any(|rule| { + rule.map(|rule| { + let input = rule.input_sequence(); + ctx.glyphs.len() == input.len() + 1 + && input + .iter() + .enumerate() + .all(|(i, value)| match_fn(ctx.glyphs[i + 1], value.get())) + }) + .unwrap_or(false) + }) + }) + .unwrap_or_default() + } +} + +impl Apply for SequenceContextFormat2<'_> { + fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { + let input_classes = self.class_def().ok(); + let glyph = ctx.buffer.cur(0).as_skrifa_glyph16(); + self.coverage().ok()?.get(glyph)?; + let index = input_classes.as_ref()?.get(glyph) as usize; + let set = self.class_seq_rule_sets().get(index)?.ok()?; + for rule in set.class_seq_rules().iter().filter_map(|rule| rule.ok()) { + let input = rule.input_sequence(); + if apply_context( + ctx, + input, + &match_class(&input_classes), + rule.seq_lookup_records(), + ) + .is_some() + { + return Some(()); + } + } + None + } +} + +impl WouldApply for SequenceContextFormat3<'_> { + fn would_apply(&self, ctx: &WouldApplyContext) -> bool { + let coverages = self.coverages(); + ctx.glyphs.len() == usize::from(coverages.len()) + 1 + && coverages + .iter() + .enumerate() + .all(|(i, coverage)| covered(coverage, ctx.glyphs[i + 1])) + } +} + +impl Apply for SequenceContextFormat3<'_> { + fn apply( + &self, + ctx: &mut crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t, + ) -> Option<()> { + let glyph = skrifa::GlyphId::from(ctx.buffer.cur(0).as_glyph().0); + let input_coverages = self.coverages(); + input_coverages.get(0).ok()?.get(glyph)?; + let input = |glyph: GlyphId, index: u16| { + input_coverages + .get(index as usize + 1) + .map(|cov| cov.get(skrifa::GlyphId::from(glyph.0)).is_some()) + .unwrap_or_default() + }; + let mut match_end = 0; + let mut match_positions = smallvec::SmallVec::from_elem(0, 4); + if match_input( + ctx, + input_coverages.len() as u16 - 1, + &input, + &mut match_end, + &mut match_positions, + None, + ) { + ctx.buffer + .unsafe_to_break_from_outbuffer(Some(ctx.buffer.idx), Some(match_end)); + apply_lookup( + ctx, + input_coverages.len() - 1, + &mut match_positions, + match_end, + self.seq_lookup_records(), + ); + Some(()) + } else { + ctx.buffer + .unsafe_to_concat(Some(ctx.buffer.idx), Some(match_end)); + None + } + } +} impl WouldApply for ChainedSequenceContextFormat1<'_> { - fn would_apply(&self, _ctx: &WouldApplyContext) -> bool { - false + fn would_apply(&self, ctx: &WouldApplyContext) -> bool { + coverage_index(self.coverage(), ctx.glyphs[0]) + .and_then(|index| { + self.chained_seq_rule_sets() + .get(index as usize) + .transpose() + .ok() + .flatten() + }) + .map(|set| { + set.chained_seq_rules().iter().any(|rule| { + rule.map(|rule| { + let input = rule.input_sequence(); + (!ctx.zero_context + || (rule.backtrack_glyph_count() == 0 + && rule.lookahead_glyph_count() == 0)) + && ctx.glyphs.len() == input.len() + 1 + && input.iter().enumerate().all(|(i, value)| { + match_glyph(ctx.glyphs[i + 1], value.get().to_u16()) + }) + }) + .unwrap_or(false) + }) + }) + .unwrap_or_default() } } @@ -30,12 +203,7 @@ impl Apply for ChainedSequenceContextFormat1<'_> { input, lookahead, [&match_glyph; 3], - rule.seq_lookup_records() - .iter() - .map(|rec| SequenceLookupRecord { - sequence_index: rec.sequence_index(), - lookup_list_index: rec.lookup_list_index(), - }), + rule.seq_lookup_records(), ) .is_some() { @@ -47,8 +215,32 @@ impl Apply for ChainedSequenceContextFormat1<'_> { } impl WouldApply for ChainedSequenceContextFormat2<'_> { - fn would_apply(&self, _ctx: &WouldApplyContext) -> bool { - false + fn would_apply(&self, ctx: &WouldApplyContext) -> bool { + let class_def = self.input_class_def().ok(); + let match_fn = &match_class(&class_def); + let class = glyph_class(self.input_class_def(), ctx.glyphs[0]); + self.chained_class_seq_rule_sets() + .get(class as usize) + .transpose() + .ok() + .flatten() + .map(|set| { + set.chained_class_seq_rules().iter().any(|rule| { + rule.map(|rule| { + let input = rule.input_sequence(); + (!ctx.zero_context + || (rule.backtrack_glyph_count() == 0 + && rule.lookahead_glyph_count() == 0)) + && ctx.glyphs.len() == input.len() + 1 + && input + .iter() + .enumerate() + .all(|(i, value)| match_fn(ctx.glyphs[i + 1], value.get())) + }) + .unwrap_or(false) + }) + }) + .unwrap_or_default() } } @@ -91,12 +283,7 @@ impl Apply for ChainedSequenceContextFormat2<'_> { &match_class(&input_classes), &match_class(&lookahead_classes), ], - rule.seq_lookup_records() - .iter() - .map(|rec| SequenceLookupRecord { - sequence_index: rec.sequence_index(), - lookup_list_index: rec.lookup_list_index(), - }), + rule.seq_lookup_records(), ) .is_some() { @@ -207,12 +394,7 @@ impl Apply for ChainedSequenceContextFormat3<'_> { input_coverages.len() - 1, &mut match_positions, match_end, - self.seq_lookup_records() - .iter() - .map(|rec| SequenceLookupRecord { - sequence_index: rec.sequence_index(), - lookup_list_index: rec.lookup_list_index(), - }), + self.seq_lookup_records(), ); Some(()) @@ -235,13 +417,50 @@ impl ToU16 for BigEndian { } } +fn apply_context( + ctx: &mut hb_ot_apply_context_t, + input: &[T], + match_func: &match_func_t, + lookups: &[SequenceLookupRecord], +) -> Option<()> { + let match_func = |glyph, index| { + let value = input.get(index as usize).unwrap().to_u16(); + match_func(glyph, value) + }; + + let mut match_end = 0; + let mut match_positions = smallvec::SmallVec::from_elem(0, 4); + + if match_input( + ctx, + input.len() as _, + &match_func, + &mut match_end, + &mut match_positions, + None, + ) { + ctx.buffer + .unsafe_to_break(Some(ctx.buffer.idx), Some(match_end)); + apply_lookup( + ctx, + usize::from(input.len()), + &mut match_positions, + match_end, + lookups, + ); + return Some(()); + } + + None +} + fn apply_chain_context( ctx: &mut hb_ot_apply_context_t, backtrack: &[T], input: &[T], lookahead: &[T], match_funcs: [&match_func_t; 3], - lookups: impl Iterator, + lookups: &[SequenceLookupRecord], ) -> Option<()> { // NOTE: Whenever something in this method changes, we also need to // change it in the `apply` implementation for ChainedContextLookup. diff --git a/src/hb/fonta/ot/gpos/mod.rs b/src/hb/fonta/ot/gpos/mod.rs index a33d246..4467249 100644 --- a/src/hb/fonta/ot/gpos/mod.rs +++ b/src/hb/fonta/ot/gpos/mod.rs @@ -1,9 +1,9 @@ //! OpenType GPOS lookups. use super::{LookupCache, LookupInfo}; -use crate::hb::{ - ot_layout::TableIndex, ot_layout_gpos_table::ValueRecordExt, - ot_layout_gsubgpos::OT::hb_ot_apply_context_t, +use crate::{ + hb::{ot_layout::TableIndex, ot_layout_gsubgpos::OT::hb_ot_apply_context_t}, + GlyphPosition, }; use skrifa::raw::{ tables::{ @@ -53,7 +53,7 @@ struct Value<'a> { data: FontData<'a>, } -impl ValueRecordExt for Value<'_> { +impl Value<'_> { fn is_empty(&self) -> bool { self.record.x_placement.is_none() && self.record.y_placement.is_none() @@ -72,11 +72,7 @@ impl ValueRecordExt for Value<'_> { worked } - fn apply_to_pos( - &self, - ctx: &mut hb_ot_apply_context_t, - pos: &mut crate::GlyphPosition, - ) -> bool { + fn apply_to_pos(&self, ctx: &mut hb_ot_apply_context_t, pos: &mut GlyphPosition) -> bool { let horizontal = ctx.buffer.direction.is_horizontal(); let mut worked = false; diff --git a/src/hb/fonta/ot/gpos/pair.rs b/src/hb/fonta/ot/gpos/pair.rs index 71014c4..11a0b54 100644 --- a/src/hb/fonta/ot/gpos/pair.rs +++ b/src/hb/fonta/ot/gpos/pair.rs @@ -1,4 +1,3 @@ -use crate::hb::ot_layout_gpos_table::ValueRecordExt; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply}; use skrifa::raw::tables::gpos::{PairPosFormat1, PairPosFormat2, PairValueRecord}; diff --git a/src/hb/fonta/ot/gpos/single.rs b/src/hb/fonta/ot/gpos/single.rs index 33cdd1a..234fceb 100644 --- a/src/hb/fonta/ot/gpos/single.rs +++ b/src/hb/fonta/ot/gpos/single.rs @@ -1,5 +1,4 @@ use super::Value; -use crate::hb::ot_layout_gpos_table::ValueRecordExt; use crate::hb::ot_layout_gsubgpos::Apply; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; use skrifa::raw::tables::gpos::{SinglePosFormat1, SinglePosFormat2}; diff --git a/src/hb/fonta/ot/lookup_cache.rs b/src/hb/fonta/ot/lookup_cache.rs index 3790dfc..a42fa75 100644 --- a/src/hb/fonta/ot/lookup_cache.rs +++ b/src/hb/fonta/ot/lookup_cache.rs @@ -182,29 +182,6 @@ impl LookupCache { lookup_type: subtable_kind as u8, digest: Default::default(), }; - // TODO: update as we add more subtables - let is_supported = match (data.is_subst, subtable_kind) { - // (true, 1) | (true, 2) | (true, 3) | (true, 4) => true, - (true, 1) | (true, 2) | (true, 3) | (true, 4) => true, - (false, 4) | (false, 6) => true, - // single pos - (false, 1) => true, - // pair pos - (false, 2) => true, - // cursive pos - (false, 3) => true, - // mark lig pos - (false, 5) => true, - // chained sequence context - (true, 6) => true, - (false, 8) => true, - // reverse chained context - (true, 8) => true, - _ => false, - }; - if !is_supported { - return Err(ReadError::MalformedData("unsupported subtable")); - } let subtable = subtable_info.materialize(data.table_data.as_bytes())?; let (coverage, coverage_offset) = subtable.coverage_and_offset()?; add_coverage_to_digest(&coverage, &mut subtable_info.digest); diff --git a/src/hb/fonta/ot/mod.rs b/src/hb/fonta/ot/mod.rs index d0237a9..b37cc38 100644 --- a/src/hb/fonta/ot/mod.rs +++ b/src/hb/fonta/ot/mod.rs @@ -1,12 +1,18 @@ use crate::hb::{ + hb_font_t, ot_layout::LayoutLookup, - ot_layout_gsubgpos::{Apply, OT::hb_ot_apply_context_t}, + ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext, OT::hb_ot_apply_context_t}, set_digest::hb_set_digest_ext, }; use skrifa::raw::{ - tables::{gdef::Gdef, variations::ItemVariationStore}, - TableProvider, + tables::{ + gdef::Gdef, + layout::{ClassDef, CoverageTable}, + variations::ItemVariationStore, + }, + ReadError, TableProvider, }; +use ttf_parser::GlyphId; mod contextual; mod gpos; @@ -96,10 +102,12 @@ impl Apply for LookupInfo { Subtable::MarkBasePos1(subtable) => subtable.apply(ctx), Subtable::MarkLigPos1(subtable) => subtable.apply(ctx), Subtable::MarkMarkPos1(subtable) => subtable.apply(ctx), + Subtable::ContextFormat1(subtable) => subtable.apply(ctx), + Subtable::ContextFormat2(subtable) => subtable.apply(ctx), + Subtable::ContextFormat3(subtable) => subtable.apply(ctx), Subtable::ChainedContextFormat1(subtable) => subtable.apply(ctx), Subtable::ChainedContextFormat2(subtable) => subtable.apply(ctx), Subtable::ChainedContextFormat3(subtable) => subtable.apply(ctx), - _ => None, }; if result.is_some() { return Some(()); @@ -108,3 +116,61 @@ impl Apply for LookupInfo { None } } + +impl LookupInfo { + pub fn would_apply(&self, face: &hb_font_t, ctx: &WouldApplyContext) -> Option { + let glyph = ctx.glyphs[0]; + if !self.digest.may_have_glyph(glyph) { + return Some(false); + } + let (table_data, lookups) = if self.is_subst { + let table = face.font.ot.gsub.as_ref()?; + (table.table.offset_data().as_bytes(), &table.lookups) + } else { + let table = face.font.ot.gpos.as_ref()?; + (table.table.offset_data().as_bytes(), &table.lookups) + }; + let subtables = lookups.subtables(self)?; + for subtable_info in subtables { + if !subtable_info.digest.may_have_glyph(glyph) { + continue; + } + let Ok(subtable) = subtable_info.materialize(table_data) else { + continue; + }; + let result = match subtable { + Subtable::SingleSubst1(subtable) => subtable.would_apply(ctx), + Subtable::SingleSubst2(subtable) => subtable.would_apply(ctx), + Subtable::MultipleSubst1(subtable) => subtable.would_apply(ctx), + Subtable::AlternateSubst1(subtable) => subtable.would_apply(ctx), + Subtable::LigatureSubst1(subtable) => subtable.would_apply(ctx), + Subtable::ReverseChainContext(subtable) => subtable.would_apply(ctx), + Subtable::ContextFormat1(subtable) => subtable.would_apply(ctx), + Subtable::ContextFormat2(subtable) => subtable.would_apply(ctx), + Subtable::ContextFormat3(subtable) => subtable.would_apply(ctx), + Subtable::ChainedContextFormat1(subtable) => subtable.would_apply(ctx), + Subtable::ChainedContextFormat2(subtable) => subtable.would_apply(ctx), + Subtable::ChainedContextFormat3(subtable) => subtable.would_apply(ctx), + _ => false, + }; + return Some(result); + } + None + } +} + +fn coverage_index(coverage: Result, gid: GlyphId) -> Option { + let gid = skrifa::GlyphId16::new(gid.0); + coverage.ok().and_then(|coverage| coverage.get(gid)) +} + +fn covered(coverage: Result, gid: GlyphId) -> bool { + coverage_index(coverage, gid).is_some() +} + +fn glyph_class(class_def: Result, gid: GlyphId) -> u16 { + let gid = skrifa::GlyphId16::new(gid.0); + class_def + .map(|class_def| class_def.get(gid)) + .unwrap_or_default() +} diff --git a/src/hb/mod.rs b/src/hb/mod.rs index d5e3801..4c199c7 100644 --- a/src/hb/mod.rs +++ b/src/hb/mod.rs @@ -16,7 +16,6 @@ pub mod common; pub mod face; mod kerning; mod machine_cursor; -mod ot; mod ot_layout; mod ot_layout_common; mod ot_layout_gpos_table; diff --git a/src/hb/ot/layout/GPOS/cursive_pos.rs b/src/hb/ot/layout/GPOS/cursive_pos.rs deleted file mode 100644 index 43c5b51..0000000 --- a/src/hb/ot/layout/GPOS/cursive_pos.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::hb::buffer::HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; -use crate::hb::ot_layout_common::lookup_flags; -use crate::hb::ot_layout_gpos_table::attach_type; -use crate::hb::ot_layout_gpos_table::AnchorExt; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply}; -use crate::{Direction, GlyphPosition}; -use ttf_parser::gpos::CursiveAdjustment; - -impl Apply for CursiveAdjustment<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let this = ctx.buffer.cur(0).as_glyph(); - - let index_this = self.coverage.get(this)?; - let entry_this = self.sets.entry(index_this)?; - - let mut iter = skipping_iterator_t::new(ctx, ctx.buffer.idx, false); - - let mut unsafe_from = 0; - if !iter.prev(Some(&mut unsafe_from)) { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(unsafe_from), Some(ctx.buffer.idx + 1)); - return None; - } - - let i = iter.index(); - let prev = ctx.buffer.info[i].as_glyph(); - let index_prev = self.coverage.get(prev)?; - let Some(exit_prev) = self.sets.exit(index_prev) else { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(iter.index()), Some(ctx.buffer.idx + 1)); - return None; - }; - - let (exit_x, exit_y) = exit_prev.get(ctx.face); - let (entry_x, entry_y) = entry_this.get(ctx.face); - - let direction = ctx.buffer.direction; - let j = ctx.buffer.idx; - ctx.buffer.unsafe_to_break(Some(i), Some(j + 1)); - - let pos = &mut ctx.buffer.pos; - match direction { - Direction::LeftToRight => { - pos[i].x_advance = exit_x + pos[i].x_offset; - let d = entry_x + pos[j].x_offset; - pos[j].x_advance -= d; - pos[j].x_offset -= d; - } - Direction::RightToLeft => { - let d = exit_x + pos[i].x_offset; - pos[i].x_advance -= d; - pos[i].x_offset -= d; - pos[j].x_advance = entry_x + pos[j].x_offset; - } - Direction::TopToBottom => { - pos[i].y_advance = exit_y + pos[i].y_offset; - let d = entry_y + pos[j].y_offset; - pos[j].y_advance -= d; - pos[j].y_offset -= d; - } - Direction::BottomToTop => { - let d = exit_y + pos[i].y_offset; - pos[i].y_advance -= d; - pos[i].y_offset -= d; - pos[j].y_advance = entry_y; - } - Direction::Invalid => {} - } - - // Cross-direction adjustment - - // We attach child to parent (think graph theory and rooted trees whereas - // the root stays on baseline and each node aligns itself against its - // parent. - // - // Optimize things for the case of RightToLeft, as that's most common in - // Arabic. - let mut child = i; - let mut parent = j; - let mut x_offset = entry_x - exit_x; - let mut y_offset = entry_y - exit_y; - - // Low bits are lookup flags, so we want to truncate. - if ctx.lookup_props as u16 & lookup_flags::RIGHT_TO_LEFT == 0 { - core::mem::swap(&mut child, &mut parent); - x_offset = -x_offset; - y_offset = -y_offset; - } - - // If child was already connected to someone else, walk through its old - // chain and reverse the link direction, such that the whole tree of its - // previous connection now attaches to new parent. Watch out for case - // where new parent is on the path from old chain... - reverse_cursive_minor_offset(pos, child, direction, parent); - - pos[child].set_attach_type(attach_type::CURSIVE); - pos[child].set_attach_chain((parent as isize - child as isize) as i16); - - ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; - if direction.is_horizontal() { - pos[child].y_offset = y_offset; - } else { - pos[child].x_offset = x_offset; - } - - // If parent was attached to child, separate them. - // https://github.com/harfbuzz/harfbuzz/issues/2469 - if pos[parent].attach_chain() == -pos[child].attach_chain() { - pos[parent].set_attach_chain(0); - - if direction.is_horizontal() { - pos[parent].y_offset = 0; - } else { - pos[parent].x_offset = 0; - } - } - - ctx.buffer.idx += 1; - Some(()) - } -} - -fn reverse_cursive_minor_offset( - pos: &mut [GlyphPosition], - i: usize, - direction: Direction, - new_parent: usize, -) { - let chain = pos[i].attach_chain(); - let attach_type = pos[i].attach_type(); - if chain == 0 || attach_type & attach_type::CURSIVE == 0 { - return; - } - - pos[i].set_attach_chain(0); - - // Stop if we see new parent in the chain. - let j = (i as isize + isize::from(chain)) as _; - if j == new_parent { - return; - } - - reverse_cursive_minor_offset(pos, j, direction, new_parent); - - if direction.is_horizontal() { - pos[j].y_offset = -pos[i].y_offset; - } else { - pos[j].x_offset = -pos[i].x_offset; - } - - pos[j].set_attach_chain(-chain); - pos[j].set_attach_type(attach_type); -} diff --git a/src/hb/ot/layout/GPOS/mark_array.rs b/src/hb/ot/layout/GPOS/mark_array.rs deleted file mode 100644 index 1b9cdda..0000000 --- a/src/hb/ot/layout/GPOS/mark_array.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::hb::buffer::HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; -use crate::hb::ot_layout_gpos_table::{attach_type, AnchorExt}; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use ttf_parser::gpos::{AnchorMatrix, MarkArray}; - -pub(crate) trait MarkArrayExt { - fn apply( - &self, - ctx: &mut hb_ot_apply_context_t, - anchors: AnchorMatrix, - mark_index: u16, - glyph_index: u16, - glyph_pos: usize, - ) -> Option<()>; -} - -impl MarkArrayExt for MarkArray<'_> { - fn apply( - &self, - ctx: &mut hb_ot_apply_context_t, - anchors: AnchorMatrix, - mark_index: u16, - glyph_index: u16, - glyph_pos: usize, - ) -> Option<()> { - // If this subtable doesn't have an anchor for this base and this class - // return `None` such that the subsequent subtables have a chance at it. - let (mark_class, mark_anchor) = self.get(mark_index)?; - let base_anchor = anchors.get(glyph_index, mark_class)?; - - let (mark_x, mark_y) = mark_anchor.get(ctx.face); - let (base_x, base_y) = base_anchor.get(ctx.face); - - ctx.buffer - .unsafe_to_break(Some(glyph_pos), Some(ctx.buffer.idx + 1)); - - let idx = ctx.buffer.idx; - let pos = ctx.buffer.cur_pos_mut(); - pos.x_offset = base_x - mark_x; - pos.y_offset = base_y - mark_y; - pos.set_attach_type(attach_type::MARK); - pos.set_attach_chain((glyph_pos as isize - idx as isize) as i16); - - ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; - ctx.buffer.idx += 1; - - Some(()) - } -} diff --git a/src/hb/ot/layout/GPOS/mark_base_pos.rs b/src/hb/ot/layout/GPOS/mark_base_pos.rs deleted file mode 100644 index 9220caf..0000000 --- a/src/hb/ot/layout/GPOS/mark_base_pos.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::hb::buffer::hb_buffer_t; -use crate::hb::ot::layout::GPOS::mark_array::MarkArrayExt; -use crate::hb::ot_layout::{ - _hb_glyph_info_get_lig_comp, _hb_glyph_info_get_lig_id, _hb_glyph_info_is_mark, - _hb_glyph_info_multiplied, -}; -use crate::hb::ot_layout_common::lookup_flags; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply}; -use ttf_parser::gpos::MarkToBaseAdjustment; - -impl Apply for MarkToBaseAdjustment<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let buffer = &ctx.buffer; - let mark_glyph = ctx.buffer.cur(0).as_glyph(); - let mark_index = self.mark_coverage.get(mark_glyph)?; - - // Due to borrowing rules, we have this piece of code before creating the - // iterator, unlike in harfbuzz. - if ctx.last_base_until > buffer.idx as u32 { - ctx.last_base_until = 0; - ctx.last_base = -1; - } - - // Now we search backwards for a non-mark glyph - // We don't use skippy_iter.prev() to avoid O(n^2) behavior. - let mut iter = skipping_iterator_t::new(ctx, 0, false); - iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS)); - - let mut j = buffer.idx; - while j > ctx.last_base_until as usize { - let mut _match = iter.match_(&buffer.info[j - 1]); - if _match == match_t::MATCH { - // https://github.com/harfbuzz/harfbuzz/issues/4124 - if !accept(buffer, j - 1) - && !self.base_coverage.contains(buffer.info[j - 1].as_glyph()) - { - _match = match_t::SKIP; - } - } - - if _match == match_t::MATCH { - ctx.last_base = j as i32 - 1; - break; - } - - j -= 1; - } - ctx.last_base_until = buffer.idx as u32; - - if ctx.last_base == -1 { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(0), Some(buffer.idx + 1)); - return None; - } - - let idx = ctx.last_base as u32; - - let info = &buffer.info; - - // Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled - let base_glyph = info[idx as usize].as_glyph(); - let Some(base_index) = self.base_coverage.get(base_glyph) else { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(idx as usize), Some(buffer.idx + 1)); - return None; - }; - - self.marks - .apply(ctx, self.anchors, mark_index, base_index, idx as usize) - } -} - -fn accept(buffer: &hb_buffer_t, idx: usize) -> bool { - /* We only want to attach to the first of a MultipleSubst sequence. - * https://github.com/harfbuzz/harfbuzz/issues/740 - * Reject others... - * ...but stop if we find a mark in the MultipleSubst sequence: - * https://github.com/harfbuzz/harfbuzz/issues/1020 */ - !_hb_glyph_info_multiplied(&buffer.info[idx]) - || 0 == _hb_glyph_info_get_lig_comp(&buffer.info[idx]) - || (idx == 0 - || _hb_glyph_info_is_mark(&buffer.info[idx - 1]) - || !_hb_glyph_info_multiplied(&buffer.info[idx - 1]) - || _hb_glyph_info_get_lig_id(&buffer.info[idx]) - != _hb_glyph_info_get_lig_id(&buffer.info[idx - 1]) - || _hb_glyph_info_get_lig_comp(&buffer.info[idx]) - != _hb_glyph_info_get_lig_comp(&buffer.info[idx - 1]) + 1) -} diff --git a/src/hb/ot/layout/GPOS/mark_lig_pos.rs b/src/hb/ot/layout/GPOS/mark_lig_pos.rs deleted file mode 100644 index 132945a..0000000 --- a/src/hb/ot/layout/GPOS/mark_lig_pos.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::hb::ot::layout::GPOS::mark_array::MarkArrayExt; -use crate::hb::ot_layout::{_hb_glyph_info_get_lig_comp, _hb_glyph_info_get_lig_id}; -use crate::hb::ot_layout_common::lookup_flags; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply}; -use ttf_parser::gpos::MarkToLigatureAdjustment; - -impl Apply for MarkToLigatureAdjustment<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let buffer = &ctx.buffer; - let mark_glyph = ctx.buffer.cur(0).as_glyph(); - let mark_index = self.mark_coverage.get(mark_glyph)?; - - // Due to borrowing rules, we have this piece of code before creating the - // iterator, unlike in harfbuzz. - if ctx.last_base_until > buffer.idx as u32 { - ctx.last_base_until = 0; - ctx.last_base = -1; - } - - // Now we search backwards for a non-mark glyph - let mut iter = skipping_iterator_t::new(ctx, 0, false); - iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS)); - - let mut j = buffer.idx; - while j > ctx.last_base_until as usize { - let mut _match = iter.match_(&buffer.info[j - 1]); - if _match == match_t::MATCH { - ctx.last_base = j as i32 - 1; - break; - } - j -= 1; - } - - ctx.last_base_until = buffer.idx as u32; - - if ctx.last_base == -1 { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(0), Some(buffer.idx + 1)); - return None; - } - - let idx = ctx.last_base as usize; - - // Checking that matched glyph is actually a ligature by GDEF is too strong; disabled - - let lig_glyph = buffer.info[idx].as_glyph(); - let Some(lig_index) = self.ligature_coverage.get(lig_glyph) else { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(idx), Some(buffer.idx + 1)); - return None; - }; - let lig_attach = self.ligature_array.get(lig_index)?; - - // Find component to attach to - let comp_count = lig_attach.rows; - if comp_count == 0 { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(idx), Some(buffer.idx + 1)); - return None; - } - - // We must now check whether the ligature ID of the current mark glyph - // is identical to the ligature ID of the found ligature. If yes, we - // can directly use the component index. If not, we attach the mark - // glyph to the last component of the ligature. - let lig_id = _hb_glyph_info_get_lig_id(&buffer.info[idx]); - let mark_id = _hb_glyph_info_get_lig_id(&buffer.cur(0)); - let mark_comp = u16::from(_hb_glyph_info_get_lig_comp(buffer.cur(0))); - let matches = lig_id != 0 && lig_id == mark_id && mark_comp > 0; - let comp_index = if matches { - mark_comp.min(comp_count) - } else { - comp_count - } - 1; - - self.marks - .apply(ctx, lig_attach, mark_index, comp_index, idx) - } -} diff --git a/src/hb/ot/layout/GPOS/mark_mark_pos.rs b/src/hb/ot/layout/GPOS/mark_mark_pos.rs deleted file mode 100644 index d48d149..0000000 --- a/src/hb/ot/layout/GPOS/mark_mark_pos.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::hb::ot::layout::GPOS::mark_array::MarkArrayExt; -use crate::hb::ot_layout::{ - _hb_glyph_info_get_lig_comp, _hb_glyph_info_get_lig_id, _hb_glyph_info_is_mark, -}; -use crate::hb::ot_layout_common::lookup_flags; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply}; -use ttf_parser::gpos::MarkToMarkAdjustment; - -impl Apply for MarkToMarkAdjustment<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let buffer = &ctx.buffer; - let mark1_glyph = ctx.buffer.cur(0).as_glyph(); - let mark1_index = self.mark1_coverage.get(mark1_glyph)?; - - // Now we search backwards for a suitable mark glyph until a non-mark glyph - let mut iter = skipping_iterator_t::new(ctx, buffer.idx, false); - iter.set_lookup_props(ctx.lookup_props & !u32::from(lookup_flags::IGNORE_FLAGS)); - - let mut unsafe_from = 0; - if !iter.prev(Some(&mut unsafe_from)) { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(unsafe_from), Some(ctx.buffer.idx + 1)); - return None; - } - - let iter_idx = iter.index(); - if !_hb_glyph_info_is_mark(&buffer.info[iter_idx]) { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(iter_idx), Some(buffer.idx + 1)); - return None; - } - - let id1 = _hb_glyph_info_get_lig_id(buffer.cur(0)); - let id2 = _hb_glyph_info_get_lig_id(&buffer.info[iter_idx]); - let comp1 = _hb_glyph_info_get_lig_comp(buffer.cur(0)); - let comp2 = _hb_glyph_info_get_lig_comp(&buffer.info[iter_idx]); - - let matches = if id1 == id2 { - // Marks belonging to the same base - // or marks belonging to the same ligature component. - id1 == 0 || comp1 == comp2 - } else { - // If ligature ids don't match, it may be the case that one of the marks - // itself is a ligature. In which case match. - (id1 > 0 && comp1 == 0) || (id2 > 0 && comp2 == 0) - }; - - if !matches { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(iter_idx), Some(buffer.idx + 1)); - return None; - } - - let mark2_glyph = buffer.info[iter_idx].as_glyph(); - let mark2_index = self.mark2_coverage.get(mark2_glyph)?; - - self.marks - .apply(ctx, self.mark2_matrix, mark1_index, mark2_index, iter_idx) - } -} diff --git a/src/hb/ot/layout/GPOS/mod.rs b/src/hb/ot/layout/GPOS/mod.rs deleted file mode 100644 index bd9b750..0000000 --- a/src/hb/ot/layout/GPOS/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub(crate) mod cursive_pos; -pub(crate) mod mark_array; -pub(crate) mod mark_base_pos; -pub(crate) mod mark_lig_pos; -pub(crate) mod mark_mark_pos; -pub(crate) mod pair_pos; -pub(crate) mod pos_lookup; -pub(crate) mod single_pos; diff --git a/src/hb/ot/layout/GPOS/pair_pos.rs b/src/hb/ot/layout/GPOS/pair_pos.rs deleted file mode 100644 index 5568de1..0000000 --- a/src/hb/ot/layout/GPOS/pair_pos.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::hb::ot_layout_gpos_table::ValueRecordExt; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply}; -use ttf_parser::gpos::{PairAdjustment, ValueRecord}; - -impl Apply for PairAdjustment<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let first_glyph = ctx.buffer.cur(0).as_glyph(); - let first_glyph_coverage_index = self.coverage().get(first_glyph)?; - - let mut iter = skipping_iterator_t::new(ctx, ctx.buffer.idx, false); - - let mut unsafe_to = 0; - if !iter.next(Some(&mut unsafe_to)) { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(unsafe_to)); - return None; - } - - let second_glyph_index = iter.index(); - let second_glyph = ctx.buffer.info[second_glyph_index].as_glyph(); - - let finish = |ctx: &mut hb_ot_apply_context_t, iter_index: &mut usize, has_record2| { - if has_record2 { - *iter_index += 1; - // https://github.com/harfbuzz/harfbuzz/issues/3824 - // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 - ctx.buffer - .unsafe_to_break(Some(ctx.buffer.idx), Some(*iter_index + 1)); - } - - ctx.buffer.idx = *iter_index; - - Some(()) - }; - - let boring = |ctx: &mut hb_ot_apply_context_t, iter_index: &mut usize, has_record2| { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(second_glyph_index + 1)); - finish(ctx, iter_index, has_record2) - }; - - let success = - |ctx: &mut hb_ot_apply_context_t, iter_index: &mut usize, flag1, flag2, has_record2| { - if flag1 || flag2 { - ctx.buffer - .unsafe_to_break(Some(ctx.buffer.idx), Some(second_glyph_index + 1)); - finish(ctx, iter_index, has_record2) - } else { - boring(ctx, iter_index, has_record2) - } - }; - - let bail = |ctx: &mut hb_ot_apply_context_t, - iter_index: &mut usize, - records: (ValueRecord, ValueRecord)| { - let has_record1 = !records.0.is_empty(); - let has_record2 = !records.1.is_empty(); - - let flag1 = has_record1 && records.0.apply(ctx, ctx.buffer.idx); - let flag2 = has_record2 && records.1.apply(ctx, second_glyph_index); - - success(ctx, iter_index, flag1, flag2, has_record2) - }; - - let records = match self { - Self::Format1 { sets, .. } => { - sets.get(first_glyph_coverage_index)?.get(second_glyph)? - } - Self::Format2 { - classes, matrix, .. - } => { - let classes = (classes.0.get(first_glyph), classes.1.get(second_glyph)); - - let records = match matrix.get(classes) { - Some(v) => v, - None => { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(iter.index() + 1)); - return None; - } - }; - - return bail(ctx, &mut iter.buf_idx, records); - } - }; - - bail(ctx, &mut iter.buf_idx, records) - } -} diff --git a/src/hb/ot/layout/GPOS/pos_lookup.rs b/src/hb/ot/layout/GPOS/pos_lookup.rs deleted file mode 100644 index aebbaa9..0000000 --- a/src/hb/ot/layout/GPOS/pos_lookup.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::hb::ot_layout::LayoutLookup; -use crate::hb::ot_layout_common::PositioningLookup; -use crate::hb::ot_layout_gsubgpos::Apply; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::set_digest::{hb_set_digest_ext, hb_set_digest_t}; - -impl LayoutLookup for PositioningLookup<'_> { - fn props(&self) -> u32 { - self.props - } - - fn is_reverse(&self) -> bool { - false - } - - fn digest(&self) -> &hb_set_digest_t { - &self.set_digest - } -} - -impl Apply for PositioningLookup<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - if self.digest().may_have_glyph(ctx.buffer.cur(0).as_glyph()) { - for subtable in &self.subtables { - if subtable.apply(ctx).is_some() { - return Some(()); - } - } - } - - None - } -} diff --git a/src/hb/ot/layout/GPOS/single_pos.rs b/src/hb/ot/layout/GPOS/single_pos.rs deleted file mode 100644 index 963b7dd..0000000 --- a/src/hb/ot/layout/GPOS/single_pos.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::hb::ot_layout_gpos_table::ValueRecordExt; -use crate::hb::ot_layout_gsubgpos::Apply; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use ttf_parser::gpos::SingleAdjustment; - -impl Apply for SingleAdjustment<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let record = match self { - Self::Format1 { coverage, value } => { - coverage.get(glyph)?; - *value - } - Self::Format2 { coverage, values } => { - let index = coverage.get(glyph)?; - values.get(index)? - } - }; - record.apply(ctx, ctx.buffer.idx); - ctx.buffer.idx += 1; - Some(()) - } -} diff --git a/src/hb/ot/layout/GSUB/alternate_set.rs b/src/hb/ot/layout/GSUB/alternate_set.rs deleted file mode 100644 index bf97a67..0000000 --- a/src/hb/ot/layout/GSUB/alternate_set.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::Apply; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_map::hb_ot_map_t; -use core::convert::TryFrom; -use ttf_parser::gsub::AlternateSet; - -impl Apply for AlternateSet<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let len = self.alternates.len(); - if len == 0 { - return None; - } - - let glyph_mask = ctx.buffer.cur(0).mask; - - // Note: This breaks badly if two features enabled this lookup together. - let shift = ctx.lookup_mask().trailing_zeros(); - let mut alt_index = (ctx.lookup_mask() & glyph_mask) >> shift; - - // If alt_index is MAX_VALUE, randomize feature if it is the rand feature. - if alt_index == hb_ot_map_t::MAX_VALUE && ctx.random { - // Maybe we can do better than unsafe-to-break all; but since we are - // changing random state, it would be hard to track that. Good 'nough. - ctx.buffer.unsafe_to_break(Some(0), Some(ctx.buffer.len)); - alt_index = ctx.random_number() % u32::from(len) + 1; - } - - let idx = u16::try_from(alt_index).ok()?.checked_sub(1)?; - ctx.replace_glyph(self.alternates.get(idx)?); - - Some(()) - } -} diff --git a/src/hb/ot/layout/GSUB/alternate_subst.rs b/src/hb/ot/layout/GSUB/alternate_subst.rs deleted file mode 100644 index d9ba879..0000000 --- a/src/hb/ot/layout/GSUB/alternate_subst.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; -use ttf_parser::gsub::AlternateSubstitution; - -// AlternateSubstFormat1::would_apply -impl WouldApply for AlternateSubstitution<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - ctx.glyphs.len() == 1 && self.coverage.get(ctx.glyphs[0]).is_some() - } -} - -// AlternateSubstFormat1::apply -impl Apply for AlternateSubstitution<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage.get(glyph)?; - let set = self.alternate_sets.get(index)?; - set.apply(ctx) - } -} diff --git a/src/hb/ot/layout/GSUB/ligature.rs b/src/hb/ot/layout/GSUB/ligature.rs deleted file mode 100644 index 35ea4a7..0000000 --- a/src/hb/ot/layout/GSUB/ligature.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{ - ligate_input, match_glyph, match_input, Apply, WouldApply, WouldApplyContext, -}; -use ttf_parser::gsub::Ligature; - -impl WouldApply for Ligature<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - ctx.glyphs.len() == usize::from(self.components.len()) + 1 - && self - .components - .into_iter() - .enumerate() - .all(|(i, comp)| ctx.glyphs[i + 1] == comp) - } -} - -impl Apply for Ligature<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - // Special-case to make it in-place and not consider this - // as a "ligated" substitution. - if self.components.is_empty() { - ctx.replace_glyph(self.glyph); - Some(()) - } else { - let f = |glyph, index| { - let value = self.components.get(index).unwrap(); - match_glyph(glyph, value.0) - }; - - let mut match_end = 0; - let mut match_positions = smallvec::SmallVec::from_elem(0, 4); - let mut total_component_count = 0; - - if !match_input( - ctx, - self.components.len(), - &f, - &mut match_end, - &mut match_positions, - Some(&mut total_component_count), - ) { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(match_end)); - return None; - } - - let count = usize::from(self.components.len()) + 1; - ligate_input( - ctx, - count, - &match_positions, - match_end, - total_component_count, - self.glyph, - ); - return Some(()); - } - } -} diff --git a/src/hb/ot/layout/GSUB/ligature_set.rs b/src/hb/ot/layout/GSUB/ligature_set.rs deleted file mode 100644 index 099d081..0000000 --- a/src/hb/ot/layout/GSUB/ligature_set.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; -use ttf_parser::gsub::LigatureSet; - -impl WouldApply for LigatureSet<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - self.into_iter().any(|lig| lig.would_apply(ctx)) - } -} - -impl Apply for LigatureSet<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - for lig in self.into_iter() { - if lig.apply(ctx).is_some() { - return Some(()); - } - } - None - - // TODO: port https://github.com/harfbuzz/harfbuzz/commit/7881eadff and - // the following commits. Since it's behind a feature flag, we ignore it - // for now and just use the simpler version. - } -} diff --git a/src/hb/ot/layout/GSUB/ligature_subst.rs b/src/hb/ot/layout/GSUB/ligature_subst.rs deleted file mode 100644 index 3df2808..0000000 --- a/src/hb/ot/layout/GSUB/ligature_subst.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; -use ttf_parser::gsub::LigatureSubstitution; - -// LigatureSubstFormat1::would_apply -impl WouldApply for LigatureSubstitution<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - self.coverage - .get(ctx.glyphs[0]) - .and_then(|index| self.ligature_sets.get(index)) - .map_or(false, |set| set.would_apply(ctx)) - } -} - -// LigatureSubstFormat1::apply -impl Apply for LigatureSubstitution<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - self.coverage - .get(glyph) - .and_then(|index| self.ligature_sets.get(index)) - .and_then(|set| set.apply(ctx)) - } -} diff --git a/src/hb/ot/layout/GSUB/mod.rs b/src/hb/ot/layout/GSUB/mod.rs deleted file mode 100644 index 6220cef..0000000 --- a/src/hb/ot/layout/GSUB/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod alternate_set; -mod alternate_subst; -mod ligature; -mod ligature_set; -mod ligature_subst; -mod multi_subst; -mod reverse_chain_single_subst; -mod sequence; -mod single_subst; -mod subst_lookup; diff --git a/src/hb/ot/layout/GSUB/multi_subst.rs b/src/hb/ot/layout/GSUB/multi_subst.rs deleted file mode 100644 index 35ea1bb..0000000 --- a/src/hb/ot/layout/GSUB/multi_subst.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; -use ttf_parser::gsub::MultipleSubstitution; - -// MultipleSubstFormat1::would_apply -impl WouldApply for MultipleSubstitution<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - ctx.glyphs.len() == 1 && self.coverage.get(ctx.glyphs[0]).is_some() - } -} - -// MultipleSubstFormat1::apply -impl Apply for MultipleSubstitution<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage.get(glyph)?; - let seq = self.sequences.get(index)?; - seq.apply(ctx) - } -} diff --git a/src/hb/ot/layout/GSUB/reverse_chain_single_subst.rs b/src/hb/ot/layout/GSUB/reverse_chain_single_subst.rs deleted file mode 100644 index 628f7ad..0000000 --- a/src/hb/ot/layout/GSUB/reverse_chain_single_subst.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::hb::ot_layout::MAX_NESTING_LEVEL; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{ - match_backtrack, match_lookahead, Apply, WouldApply, WouldApplyContext, -}; -use ttf_parser::gsub::ReverseChainSingleSubstitution; - -// ReverseChainSingleSubstFormat1::would_apply -impl WouldApply for ReverseChainSingleSubstitution<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - ctx.glyphs.len() == 1 && self.coverage.get(ctx.glyphs[0]).is_some() - } -} - -// ReverseChainSingleSubstFormat1::apply -impl Apply for ReverseChainSingleSubstitution<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage.get(glyph)?; - if index >= self.substitutes.len() { - return None; - } - - // No chaining to this type. - if ctx.nesting_level_left != MAX_NESTING_LEVEL { - return None; - } - - let subst = self.substitutes.get(index)?; - - let f1 = |glyph, index| { - let value = self.backtrack_coverages.get(index).unwrap(); - value.contains(glyph) - }; - - let f2 = |glyph, index| { - let value = self.lookahead_coverages.get(index).unwrap(); - value.contains(glyph) - }; - - let mut start_index = 0; - let mut end_index = 0; - - if match_backtrack(ctx, self.backtrack_coverages.len(), &f1, &mut start_index) { - if match_lookahead( - ctx, - self.lookahead_coverages.len(), - &f2, - ctx.buffer.idx + 1, - &mut end_index, - ) { - ctx.buffer - .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index)); - ctx.replace_glyph_inplace(subst); - - // Note: We DON'T decrease buffer.idx. The main loop does it - // for us. This is useful for preventing surprises if someone - // calls us through a Context lookup. - return Some(()); - } - } - - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(start_index), Some(end_index)); - return None; - } -} diff --git a/src/hb/ot/layout/GSUB/sequence.rs b/src/hb/ot/layout/GSUB/sequence.rs deleted file mode 100644 index 48f721e..0000000 --- a/src/hb/ot/layout/GSUB/sequence.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::hb::buffer::GlyphPropsFlags; -use crate::hb::ot_layout::{ - _hb_glyph_info_get_lig_id, _hb_glyph_info_is_ligature, - _hb_glyph_info_set_lig_props_for_component, -}; -use crate::hb::ot_layout_gsubgpos::Apply; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use ttf_parser::gsub::Sequence; - -impl Apply for Sequence<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - match self.substitutes.len() { - // Spec disallows this, but Uniscribe allows it. - // https://github.com/harfbuzz/harfbuzz/issues/253 - 0 => ctx.buffer.delete_glyph(), - - // Special-case to make it in-place and not consider this - // as a "multiplied" substitution. - 1 => ctx.replace_glyph(self.substitutes.get(0)?), - - _ => { - let class = if _hb_glyph_info_is_ligature(ctx.buffer.cur(0)) { - GlyphPropsFlags::BASE_GLYPH - } else { - GlyphPropsFlags::empty() - }; - let lig_id = _hb_glyph_info_get_lig_id(ctx.buffer.cur(0)); - - for (i, subst) in self.substitutes.into_iter().enumerate() { - // If is attached to a ligature, don't disturb that. - // https://github.com/harfbuzz/harfbuzz/issues/3069 - if lig_id == 0 { - // Index is truncated to 4 bits anway, so we can safely cast to u8. - _hb_glyph_info_set_lig_props_for_component(ctx.buffer.cur_mut(0), i as u8); - } - ctx.output_glyph_for_component(subst, class); - } - - ctx.buffer.skip_glyph(); - } - } - Some(()) - } -} diff --git a/src/hb/ot/layout/GSUB/single_subst.rs b/src/hb/ot/layout/GSUB/single_subst.rs deleted file mode 100644 index 9587cd0..0000000 --- a/src/hb/ot/layout/GSUB/single_subst.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; -use ttf_parser::gsub::SingleSubstitution; -use ttf_parser::GlyphId; - -// SingleSubstFormat1::would_apply -// SingleSubstFormat2::would_apply -impl WouldApply for SingleSubstitution<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - ctx.glyphs.len() == 1 && self.coverage().get(ctx.glyphs[0]).is_some() - } -} - -// SingleSubstFormat1::apply -// SingleSubstFormat2::apply -impl Apply for SingleSubstitution<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let subst = match *self { - Self::Format1 { coverage, delta } => { - coverage.get(glyph)?; - // According to the Adobe Annotated OpenType Suite, result is always - // limited to 16bit, so we explicitly want to truncate. - GlyphId((i32::from(glyph.0) + i32::from(delta)) as u16) - } - Self::Format2 { - coverage, - substitutes, - } => { - let index = coverage.get(glyph)?; - substitutes.get(index)? - } - }; - - ctx.replace_glyph(subst); - Some(()) - } -} diff --git a/src/hb/ot/layout/GSUB/subst_lookup.rs b/src/hb/ot/layout/GSUB/subst_lookup.rs deleted file mode 100644 index a83744e..0000000 --- a/src/hb/ot/layout/GSUB/subst_lookup.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::hb::ot_layout::LayoutLookup; -use crate::hb::ot_layout_common::SubstLookup; -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; -use crate::hb::set_digest::{hb_set_digest_ext, hb_set_digest_t}; - -impl LayoutLookup for SubstLookup<'_> { - fn props(&self) -> u32 { - self.props - } - - fn is_reverse(&self) -> bool { - self.reverse - } - - fn digest(&self) -> &hb_set_digest_t { - &self.set_digest - } -} - -impl WouldApply for SubstLookup<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - self.digest().may_have_glyph(ctx.glyphs[0]) - && self - .subtables - .iter() - .any(|subtable| subtable.would_apply(ctx)) - } -} - -impl Apply for SubstLookup<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - if self.digest().may_have_glyph(ctx.buffer.cur(0).as_glyph()) { - for subtable in &self.subtables { - if subtable.apply(ctx).is_some() { - return Some(()); - } - } - } - - None - } -} diff --git a/src/hb/ot/layout/GSUB/subst_lookup_subtable.rs b/src/hb/ot/layout/GSUB/subst_lookup_subtable.rs deleted file mode 100644 index 215c1b2..0000000 --- a/src/hb/ot/layout/GSUB/subst_lookup_subtable.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; -use ttf_parser::gsub::SubstitutionSubtable; - -impl WouldApply for SubstitutionSubtable<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - match self { - Self::Single(t) => t.would_apply(ctx), - Self::Multiple(t) => t.would_apply(ctx), - Self::Alternate(t) => t.would_apply(ctx), - Self::Ligature(t) => t.would_apply(ctx), - Self::Context(t) => t.would_apply(ctx), - Self::ChainContext(t) => t.would_apply(ctx), - Self::ReverseChainSingle(t) => t.would_apply(ctx), - } - } -} - -impl Apply for SubstitutionSubtable<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - match self { - Self::Single(t) => t.apply(ctx), - Self::Multiple(t) => t.apply(ctx), - Self::Alternate(t) => t.apply(ctx), - Self::Ligature(t) => t.apply(ctx), - Self::Context(t) => t.apply(ctx), - Self::ChainContext(t) => t.apply(ctx), - Self::ReverseChainSingle(t) => t.apply(ctx), - } - } -} diff --git a/src/hb/ot/layout/mod.rs b/src/hb/ot/layout/mod.rs deleted file mode 100644 index e2b9c5e..0000000 --- a/src/hb/ot/layout/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod GPOS; -mod GSUB; diff --git a/src/hb/ot/mod.rs b/src/hb/ot/mod.rs deleted file mode 100644 index 7f01a9e..0000000 --- a/src/hb/ot/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod layout; diff --git a/src/hb/ot_layout.rs b/src/hb/ot_layout.rs index 0921060..649181c 100644 --- a/src/hb/ot_layout.rs +++ b/src/hb/ot_layout.rs @@ -266,45 +266,6 @@ pub fn apply_layout_table( } } -/// Applies the lookups in the given GSUB or GPOS table. -pub fn apply_layout_table2( - plan: &hb_ot_shape_plan_t, - face: &hb_font_t, - buffer: &mut hb_buffer_t, - table: Option<&T>, - table2: Option<&T2>, -) { - let mut ctx = OT::hb_ot_apply_context_t::new(T::INDEX, face, buffer); - - for (stage_index, stage) in plan.ot_map.stages(T::INDEX).iter().enumerate() { - for lookup in plan.ot_map.stage_lookups(T::INDEX, stage_index) { - ctx.lookup_index = lookup.index; - ctx.set_lookup_mask(lookup.mask); - ctx.auto_zwj = lookup.auto_zwj; - ctx.auto_zwnj = lookup.auto_zwnj; - - ctx.random = lookup.random; - ctx.per_syllable = lookup.per_syllable; - - if let Some(table) = &table2 { - if let Some(lookup) = table.get_lookup(lookup.index) { - apply_string::(&mut ctx, lookup); - continue; - } - } - if let Some(table) = &table { - if let Some(lookup) = table.get_lookup(lookup.index) { - apply_string::(&mut ctx, lookup); - } - } - } - - if let Some(func) = stage.pause_func { - func(plan, face, ctx.buffer); - } - } -} - fn apply_string(ctx: &mut OT::hb_ot_apply_context_t, lookup: &T::Lookup) { if ctx.buffer.is_empty() || ctx.lookup_mask() == 0 { return; diff --git a/src/hb/ot_layout_common.rs b/src/hb/ot_layout_common.rs index 63b8c87..4166ad2 100644 --- a/src/hb/ot_layout_common.rs +++ b/src/hb/ot_layout_common.rs @@ -1,9 +1,3 @@ -use crate::hb::set_digest::{hb_set_digest_ext, hb_set_digest_t}; -use alloc::vec::Vec; -use ttf_parser::gpos::PositioningSubtable; -use ttf_parser::gsub::SubstitutionSubtable; -use ttf_parser::opentype_layout::{Coverage, Lookup}; - #[allow(dead_code)] pub mod lookup_flags { pub const RIGHT_TO_LEFT: u16 = 0x0001; @@ -18,121 +12,21 @@ pub mod lookup_flags { #[derive(Clone)] pub struct PositioningTable<'a> { pub inner: ttf_parser::opentype_layout::LayoutTable<'a>, - pub lookups: Vec>, } impl<'a> PositioningTable<'a> { pub fn new(inner: ttf_parser::opentype_layout::LayoutTable<'a>) -> Self { - let lookups = inner - .lookups - .into_iter() - .map(PositioningLookup::parse) - .collect(); - - Self { inner, lookups } - } -} - -pub trait CoverageExt { - fn collect(&self, set_digest: &mut hb_set_digest_t); -} - -impl CoverageExt for Coverage<'_> { - /// Collect this coverage table into a set digest. - fn collect(&self, set_digest: &mut hb_set_digest_t) { - match *self { - Self::Format1 { glyphs } => { - set_digest.add_array(glyphs); - } - Self::Format2 { records } => { - for record in records { - set_digest.add_range(record.start, record.end); - } - } - } - } -} - -#[derive(Clone)] -pub struct PositioningLookup<'a> { - pub subtables: Vec>, - pub set_digest: hb_set_digest_t, - pub props: u32, -} - -impl<'a> PositioningLookup<'a> { - pub fn parse(lookup: Lookup<'a>) -> Self { - let subtables: Vec<_> = lookup - .subtables - .into_iter::() - .collect(); - - let mut set_digest = hb_set_digest_t::new(); - for subtable in &subtables { - subtable.coverage().collect(&mut set_digest); - } - - Self { - subtables, - set_digest, - props: lookup_props(lookup), - } + Self { inner } } } #[derive(Clone)] pub struct SubstitutionTable<'a> { pub inner: ttf_parser::opentype_layout::LayoutTable<'a>, - pub lookups: Vec>, } impl<'a> SubstitutionTable<'a> { pub fn new(inner: ttf_parser::opentype_layout::LayoutTable<'a>) -> Self { - let lookups = inner.lookups.into_iter().map(SubstLookup::parse).collect(); - - Self { inner, lookups } - } -} - -#[derive(Clone)] -pub struct SubstLookup<'a> { - pub subtables: Vec>, - pub set_digest: hb_set_digest_t, - pub reverse: bool, - pub props: u32, -} - -impl<'a> SubstLookup<'a> { - pub fn parse(lookup: Lookup<'a>) -> Self { - let subtables: Vec<_> = lookup - .subtables - .into_iter::() - .collect(); - - let mut set_digest = hb_set_digest_t::new(); - let mut reverse = !subtables.is_empty(); - - for subtable in &subtables { - subtable.coverage().collect(&mut set_digest); - reverse &= subtable.is_reverse(); - } - - Self { - subtables, - set_digest, - reverse, - props: lookup_props(lookup), - } - } -} - -// lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and -// higher 16-bit is mark-filtering-set if the lookup uses one. -// Not to be confused with glyph_props which is very similar. */ -fn lookup_props(lookup: Lookup) -> u32 { - let mut props = u32::from(lookup.flags.0); - if let Some(set) = lookup.mark_filtering_set { - props |= u32::from(set) << 16; + Self { inner } } - props } diff --git a/src/hb/ot_layout_gpos_table.rs b/src/hb/ot_layout_gpos_table.rs index a95bd8f..9f584cd 100644 --- a/src/hb/ot_layout_gpos_table.rs +++ b/src/hb/ot_layout_gpos_table.rs @@ -4,153 +4,11 @@ use core_maths::CoreFloat; use super::buffer::*; use super::hb_font_t; use super::ot_layout::*; -use super::ot_layout_common::{PositioningLookup, PositioningTable}; -use super::ot_layout_gsubgpos::{Apply, OT::hb_ot_apply_context_t}; use super::ot_shape_plan::hb_ot_shape_plan_t; use crate::Direction; -use ttf_parser::gpos::*; -use ttf_parser::opentype_layout::LookupIndex; pub fn position(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) { - //apply_layout_table(plan, face, buffer, face.gpos.as_ref()); - apply_layout_table2( - plan, - face, - buffer, - face.gpos.as_ref(), - face.font.ot.gpos.as_ref(), - ); -} - -pub(crate) trait ValueRecordExt { - fn is_empty(&self) -> bool; - fn apply(&self, ctx: &mut hb_ot_apply_context_t, idx: usize) -> bool; - fn apply_to_pos(&self, ctx: &mut hb_ot_apply_context_t, pos: &mut GlyphPosition) -> bool; -} - -impl ValueRecordExt for ValueRecord<'_> { - fn is_empty(&self) -> bool { - self.x_placement == 0 - && self.y_placement == 0 - && self.x_advance == 0 - && self.y_advance == 0 - && self.x_placement_device.is_none() - && self.y_placement_device.is_none() - && self.x_advance_device.is_none() - && self.y_advance_device.is_none() - } - - fn apply(&self, ctx: &mut hb_ot_apply_context_t, idx: usize) -> bool { - let mut pos = ctx.buffer.pos[idx]; - let worked = self.apply_to_pos(ctx, &mut pos); - ctx.buffer.pos[idx] = pos; - worked - } - - fn apply_to_pos(&self, ctx: &mut hb_ot_apply_context_t, pos: &mut GlyphPosition) -> bool { - let horizontal = ctx.buffer.direction.is_horizontal(); - let mut worked = false; - - if self.x_placement != 0 { - pos.x_offset += i32::from(self.x_placement); - worked = true; - } - - if self.y_placement != 0 { - pos.y_offset += i32::from(self.y_placement); - worked = true; - } - - if self.x_advance != 0 && horizontal { - pos.x_advance += i32::from(self.x_advance); - worked = true; - } - - if self.y_advance != 0 && !horizontal { - // y_advance values grow downward but font-space grows upward, hence negation - pos.y_advance -= i32::from(self.y_advance); - worked = true; - } - - { - let (ppem_x, ppem_y) = ctx.face.pixels_per_em().unwrap_or((0, 0)); - let coords = ctx.face.ttfp_face.variation_coordinates().len(); - let use_x_device = ppem_x != 0 || coords != 0; - let use_y_device = ppem_y != 0 || coords != 0; - - if use_x_device { - if let Some(device) = self.x_placement_device { - pos.x_offset += device.get_x_delta(ctx.face).unwrap_or(0); - worked = true; // TODO: even when 0? - } - } - - if use_y_device { - if let Some(device) = self.y_placement_device { - pos.y_offset += device.get_y_delta(ctx.face).unwrap_or(0); - worked = true; - } - } - - if horizontal && use_x_device { - if let Some(device) = self.x_advance_device { - pos.x_advance += device.get_x_delta(ctx.face).unwrap_or(0); - worked = true; - } - } - - if !horizontal && use_y_device { - if let Some(device) = self.y_advance_device { - // y_advance values grow downward but face-space grows upward, hence negation - pos.y_advance -= device.get_y_delta(ctx.face).unwrap_or(0); - worked = true; - } - } - } - - worked - } -} - -pub(crate) trait AnchorExt { - fn get(&self, face: &hb_font_t) -> (i32, i32); -} - -impl AnchorExt for Anchor<'_> { - fn get(&self, face: &hb_font_t) -> (i32, i32) { - let mut x = i32::from(self.x); - let mut y = i32::from(self.y); - - if self.x_device.is_some() || self.y_device.is_some() { - let (ppem_x, ppem_y) = face.pixels_per_em().unwrap_or((0, 0)); - let coords = face.ttfp_face.variation_coordinates().len(); - - if let Some(device) = self.x_device { - if ppem_x != 0 || coords != 0 { - x += device.get_x_delta(face).unwrap_or(0); - } - } - - if let Some(device) = self.y_device { - if ppem_y != 0 || coords != 0 { - y += device.get_y_delta(face).unwrap_or(0); - } - } - } - - (x, y) - } -} - -impl<'a> LayoutTable for PositioningTable<'a> { - const INDEX: TableIndex = TableIndex::GPOS; - const IN_PLACE: bool = true; - - type Lookup = PositioningLookup<'a>; - - fn get_lookup(&self, index: LookupIndex) -> Option<&Self::Lookup> { - self.lookups.get(usize::from(index)) - } + apply_layout_table(plan, face, buffer, face.font.ot.gpos.as_ref()); } pub mod attach_type { @@ -158,86 +16,6 @@ pub mod attach_type { pub const CURSIVE: u8 = 2; } -/// Just like TryFrom, but for numeric types not supported by the Rust's std. -pub(crate) trait TryNumFrom: Sized { - /// Casts between numeric types. - fn try_num_from(_: T) -> Option; -} - -impl TryNumFrom for i32 { - #[inline] - fn try_num_from(v: f32) -> Option { - // Based on https://github.com/rust-num/num-traits/blob/master/src/cast.rs - - // Float as int truncates toward zero, so we want to allow values - // in the exclusive range `(MIN-1, MAX+1)`. - - // We can't represent `MIN-1` exactly, but there's no fractional part - // at this magnitude, so we can just use a `MIN` inclusive boundary. - const MIN: f32 = core::i32::MIN as f32; - // We can't represent `MAX` exactly, but it will round up to exactly - // `MAX+1` (a power of two) when we cast it. - const MAX_P1: f32 = core::i32::MAX as f32; - if v >= MIN && v < MAX_P1 { - Some(v as i32) - } else { - None - } - } -} - -pub(crate) trait DeviceExt { - fn get_x_delta(&self, face: &hb_font_t) -> Option; - fn get_y_delta(&self, face: &hb_font_t) -> Option; -} - -impl DeviceExt for Device<'_> { - fn get_x_delta(&self, face: &hb_font_t) -> Option { - match self { - Device::Hinting(hinting) => hinting.x_delta(face.units_per_em, face.pixels_per_em()), - Device::Variation(variation) => face - .tables() - .gdef? - .glyph_variation_delta( - variation.outer_index, - variation.inner_index, - face.variation_coordinates(), - ) - .and_then(|float| i32::try_num_from(float.round())), - } - } - - fn get_y_delta(&self, face: &hb_font_t) -> Option { - match self { - Device::Hinting(hinting) => hinting.y_delta(face.units_per_em, face.pixels_per_em()), - Device::Variation(variation) => face - .tables() - .gdef? - .glyph_variation_delta( - variation.outer_index, - variation.inner_index, - face.variation_coordinates(), - ) - .and_then(|float| i32::try_num_from(float.round())), - } - } -} - -impl Apply for PositioningSubtable<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - match self { - Self::Single(t) => t.apply(ctx), - Self::Pair(t) => t.apply(ctx), - Self::Cursive(t) => t.apply(ctx), - Self::MarkToBase(t) => t.apply(ctx), - Self::MarkToLigature(t) => t.apply(ctx), - Self::MarkToMark(t) => t.apply(ctx), - Self::Context(t) => t.apply(ctx), - Self::ChainContext(t) => t.apply(ctx), - } - } -} - fn propagate_attachment_offsets( pos: &mut [GlyphPosition], len: usize, diff --git a/src/hb/ot_layout_gsub_table.rs b/src/hb/ot_layout_gsub_table.rs index b82120b..1a26671 100644 --- a/src/hb/ot_layout_gsub_table.rs +++ b/src/hb/ot_layout_gsub_table.rs @@ -1,60 +1,8 @@ -use ttf_parser::gsub::*; -use ttf_parser::opentype_layout::LookupIndex; - use super::buffer::hb_buffer_t; use super::hb_font_t; use super::ot_layout::*; -use super::ot_layout_common::{SubstLookup, SubstitutionTable}; -use super::ot_layout_gsubgpos::*; use super::ot_shape_plan::hb_ot_shape_plan_t; -use OT::hb_ot_apply_context_t; pub fn substitute(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) { - // apply_layout_table(plan, face, buffer, face.gsub.as_ref()); - apply_layout_table2( - plan, - face, - buffer, - face.gsub.as_ref(), - face.font.ot.gsub.as_ref(), - ); -} - -impl<'a> LayoutTable for SubstitutionTable<'a> { - const INDEX: TableIndex = TableIndex::GSUB; - const IN_PLACE: bool = false; - - type Lookup = SubstLookup<'a>; - - fn get_lookup(&self, index: LookupIndex) -> Option<&Self::Lookup> { - self.lookups.get(usize::from(index)) - } -} - -impl WouldApply for SubstitutionSubtable<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - match self { - Self::Single(t) => t.would_apply(ctx), - Self::Multiple(t) => t.would_apply(ctx), - Self::Alternate(t) => t.would_apply(ctx), - Self::Ligature(t) => t.would_apply(ctx), - Self::Context(t) => t.would_apply(ctx), - Self::ChainContext(t) => t.would_apply(ctx), - Self::ReverseChainSingle(t) => t.would_apply(ctx), - } - } -} - -impl Apply for SubstitutionSubtable<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - match self { - Self::Single(t) => t.apply(ctx), - Self::Multiple(t) => t.apply(ctx), - Self::Alternate(t) => t.apply(ctx), - Self::Ligature(t) => t.apply(ctx), - Self::Context(t) => t.apply(ctx), - Self::ChainContext(t) => t.apply(ctx), - Self::ReverseChainSingle(t) => t.apply(ctx), - } - } + apply_layout_table(plan, face, buffer, face.font.ot.gsub.as_ref()); } diff --git a/src/hb/ot_layout_gsubgpos.rs b/src/hb/ot_layout_gsubgpos.rs index 19263ee..9ad459d 100644 --- a/src/hb/ot_layout_gsubgpos.rs +++ b/src/hb/ot_layout_gsubgpos.rs @@ -1,7 +1,8 @@ //! Matching of glyph patterns. +use skrifa::raw::tables::layout::SequenceLookupRecord; use ttf_parser::opentype_layout::*; -use ttf_parser::{GlyphId, LazyArray16}; +use ttf_parser::GlyphId; use super::buffer::hb_glyph_info_t; use super::buffer::{hb_buffer_t, GlyphPropsFlags}; @@ -396,456 +397,12 @@ impl<'a, 'b> skipping_iterator_t<'a, 'b> { } } -impl WouldApply for ContextLookup<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - let glyph = ctx.glyphs[0]; - match *self { - Self::Format1 { coverage, sets } => coverage - .get(glyph) - .and_then(|index| sets.get(index)) - .map_or(false, |set| set.would_apply(ctx, &match_glyph)), - Self::Format2 { classes, sets, .. } => { - let class = classes.get(glyph); - sets.get(class) - .map_or(false, |set| set.would_apply(ctx, &match_class(classes))) - } - Self::Format3 { coverages, .. } => { - ctx.glyphs.len() == usize::from(coverages.len()) + 1 - && coverages - .into_iter() - .enumerate() - .all(|(i, coverage)| coverage.get(ctx.glyphs[i + 1]).is_some()) - } - } - } -} - -impl Apply for ContextLookup<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - match *self { - Self::Format1 { coverage, sets } => { - coverage.get(glyph)?; - let set = coverage.get(glyph).and_then(|index| sets.get(index))?; - set.apply(ctx, &match_glyph) - } - Self::Format2 { - coverage, - classes, - sets, - } => { - coverage.get(glyph)?; - let class = classes.get(glyph); - let set = sets.get(class)?; - set.apply(ctx, &match_class(classes)) - } - Self::Format3 { - coverage, - coverages, - lookups, - } => { - coverage.get(glyph)?; - let coverages_len = coverages.len(); - - let match_func = |glyph, index| { - let coverage = coverages.get(index).unwrap(); - coverage.get(glyph).is_some() - }; - - let mut match_end = 0; - let mut match_positions = smallvec::SmallVec::from_elem(0, 4); - - if match_input( - ctx, - coverages_len, - &match_func, - &mut match_end, - &mut match_positions, - None, - ) { - ctx.buffer - .unsafe_to_break(Some(ctx.buffer.idx), Some(match_end)); - apply_lookup( - ctx, - usize::from(coverages_len), - &mut match_positions, - match_end, - lookups.into_iter(), - ); - return Some(()); - } else { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(match_end)); - return None; - } - } - } - } -} - -trait SequenceRuleSetExt { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool; - fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()>; -} - -impl SequenceRuleSetExt for SequenceRuleSet<'_> { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool { - self.into_iter() - .any(|rule| rule.would_apply(ctx, match_func)) - } - - fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()> { - if self - .into_iter() - .any(|rule| rule.apply(ctx, match_func).is_some()) - { - Some(()) - } else { - None - } - } -} - -trait SequenceRuleExt { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool; - fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()>; -} - -impl SequenceRuleExt for SequenceRule<'_> { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool { - ctx.glyphs.len() == usize::from(self.input.len()) + 1 - && self - .input - .into_iter() - .enumerate() - .all(|(i, value)| match_func(ctx.glyphs[i + 1], value)) - } - - fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()> { - apply_context(ctx, self.input, match_func, self.lookups) - - // TODO: Port optimized version from https://github.com/harfbuzz/harfbuzz/commit/645fabd10 - } -} - -impl WouldApply for ChainedContextLookup<'_> { - fn would_apply(&self, ctx: &WouldApplyContext) -> bool { - let glyph_id = ctx.glyphs[0]; - match *self { - Self::Format1 { coverage, sets } => coverage - .get(glyph_id) - .and_then(|index| sets.get(index)) - .map_or(false, |set| set.would_apply(ctx, &match_glyph)), - Self::Format2 { - input_classes, - sets, - .. - } => { - let class = input_classes.get(glyph_id); - sets.get(class).map_or(false, |set| { - set.would_apply(ctx, &match_class(input_classes)) - }) - } - Self::Format3 { - backtrack_coverages, - input_coverages, - lookahead_coverages, - .. - } => { - (!ctx.zero_context - || (backtrack_coverages.len() == 0 && lookahead_coverages.len() == 0)) - && (ctx.glyphs.len() == usize::from(input_coverages.len()) + 1 - && input_coverages - .into_iter() - .enumerate() - .all(|(i, coverage)| coverage.contains(ctx.glyphs[i + 1]))) - } - } - } -} - -impl Apply for ChainedContextLookup<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - match *self { - Self::Format1 { coverage, sets } => { - let index = coverage.get(glyph)?; - let set = sets.get(index)?; - set.apply(ctx, [&match_glyph, &match_glyph, &match_glyph]) - } - Self::Format2 { - coverage, - backtrack_classes, - input_classes, - lookahead_classes, - sets, - } => { - coverage.get(glyph)?; - let class = input_classes.get(glyph); - let set = sets.get(class)?; - set.apply( - ctx, - [ - &match_class(backtrack_classes), - &match_class(input_classes), - &match_class(lookahead_classes), - ], - ) - } - Self::Format3 { - coverage, - backtrack_coverages, - input_coverages, - lookahead_coverages, - lookups, - } => { - coverage.get(glyph)?; - - let back = |glyph, index| { - let coverage = backtrack_coverages.get(index).unwrap(); - coverage.contains(glyph) - }; - - let ahead = |glyph, index| { - let coverage = lookahead_coverages.get(index).unwrap(); - coverage.contains(glyph) - }; - - let input = |glyph, index| { - let coverage = input_coverages.get(index).unwrap(); - coverage.contains(glyph) - }; - - let mut end_index = ctx.buffer.idx; - let mut match_end = 0; - let mut match_positions = smallvec::SmallVec::from_elem(0, 4); - - let input_matches = match_input( - ctx, - input_coverages.len(), - &input, - &mut match_end, - &mut match_positions, - None, - ); - - if input_matches { - end_index = match_end; - } - - if !(input_matches - && match_lookahead( - ctx, - lookahead_coverages.len(), - &ahead, - match_end, - &mut end_index, - )) - { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(end_index)); - return None; - } - - let mut start_index = ctx.buffer.out_len; - - if !match_backtrack(ctx, backtrack_coverages.len(), &back, &mut start_index) { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(start_index), Some(end_index)); - return None; - } - - ctx.buffer - .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index)); - apply_lookup( - ctx, - usize::from(input_coverages.len()), - &mut match_positions, - match_end, - lookups.into_iter(), - ); - - Some(()) - } - } - } -} - -trait ChainRuleSetExt { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool; - fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_funcs: [&match_func_t; 3]) - -> Option<()>; -} - -impl ChainRuleSetExt for ChainedSequenceRuleSet<'_> { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool { - self.into_iter() - .any(|rule| rule.would_apply(ctx, match_func)) - } - - fn apply( - &self, - ctx: &mut hb_ot_apply_context_t, - match_funcs: [&match_func_t; 3], - ) -> Option<()> { - if self - .into_iter() - .any(|rule| rule.apply(ctx, match_funcs).is_some()) - { - Some(()) - } else { - None - } - - // TODO: Port optimized version from https://github.com/harfbuzz/harfbuzz/commit/77080f86f - } -} - -trait ChainRuleExt { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool; - fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_funcs: [&match_func_t; 3]) - -> Option<()>; -} - -impl ChainRuleExt for ChainedSequenceRule<'_> { - fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool { - (!ctx.zero_context || (self.backtrack.len() == 0 && self.lookahead.len() == 0)) - && (ctx.glyphs.len() == usize::from(self.input.len()) + 1 - && self - .input - .into_iter() - .enumerate() - .all(|(i, value)| match_func(ctx.glyphs[i + 1], value))) - } - - fn apply( - &self, - ctx: &mut hb_ot_apply_context_t, - match_funcs: [&match_func_t; 3], - ) -> Option<()> { - apply_chain_context( - ctx, - self.backtrack, - self.input, - self.lookahead, - match_funcs, - self.lookups, - ) - } -} - -fn apply_context( - ctx: &mut hb_ot_apply_context_t, - input: LazyArray16, - match_func: &match_func_t, - lookups: LazyArray16, -) -> Option<()> { - let match_func = |glyph, index| { - let value = input.get(index).unwrap(); - match_func(glyph, value) - }; - - let mut match_end = 0; - let mut match_positions = smallvec::SmallVec::from_elem(0, 4); - - if match_input( - ctx, - input.len(), - &match_func, - &mut match_end, - &mut match_positions, - None, - ) { - ctx.buffer - .unsafe_to_break(Some(ctx.buffer.idx), Some(match_end)); - apply_lookup( - ctx, - usize::from(input.len()), - &mut match_positions, - match_end, - lookups.into_iter(), - ); - return Some(()); - } - - None -} - -fn apply_chain_context( - ctx: &mut hb_ot_apply_context_t, - backtrack: LazyArray16, - input: LazyArray16, - lookahead: LazyArray16, - match_funcs: [&match_func_t; 3], - lookups: LazyArray16, -) -> Option<()> { - // NOTE: Whenever something in this method changes, we also need to - // change it in the `apply` implementation for ChainedContextLookup. - let f1 = |glyph, index| { - let value = backtrack.get(index).unwrap(); - match_funcs[0](glyph, value) - }; - - let f2 = |glyph, index| { - let value = lookahead.get(index).unwrap(); - match_funcs[2](glyph, value) - }; - - let f3 = |glyph, index| { - let value = input.get(index).unwrap(); - match_funcs[1](glyph, value) - }; - - let mut end_index = ctx.buffer.idx; - let mut match_end = 0; - let mut match_positions = smallvec::SmallVec::from_elem(0, 4); - - let input_matches = match_input( - ctx, - input.len(), - &f3, - &mut match_end, - &mut match_positions, - None, - ); - - if input_matches { - end_index = match_end; - } - - if !(input_matches && match_lookahead(ctx, lookahead.len(), &f2, match_end, &mut end_index)) { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(end_index)); - return None; - } - - let mut start_index = ctx.buffer.out_len; - - if !match_backtrack(ctx, backtrack.len(), &f1, &mut start_index) { - ctx.buffer - .unsafe_to_concat_from_outbuffer(Some(start_index), Some(end_index)); - return None; - } - - ctx.buffer - .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index)); - apply_lookup( - ctx, - usize::from(input.len()), - &mut match_positions, - match_end, - lookups.into_iter(), - ); - - Some(()) -} - pub(super) fn apply_lookup( ctx: &mut hb_ot_apply_context_t, input_len: usize, match_positions: &mut smallvec::SmallVec<[usize; 4]>, match_end: usize, - lookups: impl Iterator, + lookups: &[SequenceLookupRecord], ) { let mut count = input_len + 1; @@ -872,7 +429,7 @@ pub(super) fn apply_lookup( break; } - let idx = usize::from(record.sequence_index); + let idx = usize::from(record.sequence_index.get()); if idx >= count { continue; } @@ -892,7 +449,7 @@ pub(super) fn apply_lookup( break; } - if ctx.recurse(record.lookup_list_index).is_none() { + if ctx.recurse(record.lookup_list_index.get()).is_none() { continue; } @@ -977,11 +534,6 @@ pub(super) fn apply_lookup( ctx.buffer.move_to(end); } -/// Value represents glyph class. -fn match_class<'a>(class_def: ClassDefinition<'a>) -> impl Fn(GlyphId, u16) -> bool + 'a { - move |glyph, value| class_def.get(glyph) == value -} - /// Find out whether a lookup would be applied. pub trait WouldApply { /// Whether the lookup would be applied. @@ -1093,14 +645,7 @@ pub mod OT { self.lookup_props = lookup.props(); lookup.apply(self) } else { - self.face - .gsub - .as_ref() - .and_then(|table| table.get_lookup(sub_lookup_index)) - .and_then(|lookup| { - self.lookup_props = lookup.props(); - lookup.apply(self) - }) + None } } TableIndex::GPOS => { @@ -1115,14 +660,7 @@ pub mod OT { self.lookup_props = lookup.props(); lookup.apply(self) } else { - self.face - .gpos - .as_ref() - .and_then(|table| table.get_lookup(sub_lookup_index)) - .and_then(|lookup| { - self.lookup_props = lookup.props(); - lookup.apply(self) - }) + None } } }; diff --git a/src/hb/ot_shaper_indic.rs b/src/hb/ot_shaper_indic.rs index d328482..a4c4bfb 100644 --- a/src/hb/ot_shaper_indic.rs +++ b/src/hb/ot_shaper_indic.rs @@ -8,7 +8,7 @@ use ttf_parser::GlyphId; use super::algs::*; use super::buffer::hb_buffer_t; use super::ot_layout::*; -use super::ot_layout_gsubgpos::{WouldApply, WouldApplyContext}; +use super::ot_layout_gsubgpos::WouldApplyContext; use super::ot_map::*; use super::ot_shape::*; use super::ot_shape_normalize::*; @@ -422,10 +422,12 @@ impl IndicWouldSubstituteFeature { zero_context: self.zero_context, }; if face + .font + .ot .gsub .as_ref() .and_then(|table| table.get_lookup(lookup.index)) - .map_or(false, |lookup| lookup.would_apply(&ctx)) + .map_or(false, |lookup| lookup.would_apply(face, &ctx) == Some(true)) { return true; }