1use super::aat::layout::DELETED_GLYPH;
2use alloc::boxed::Box;
3use read_fonts::{
4 tables::{
5 aat,
6 kern::{Subtable, Subtable0, Subtable2, Subtable3, SubtableKind},
7 },
8 types::{GlyphId, GlyphId16},
9};
10
11use super::aat::layout_common::{AatApplyContext, ClassCache, START_OF_TEXT};
12use super::aat::layout_kerx_table::SimpleKerning;
13use super::buffer::*;
14use super::face::Scale;
15use super::ot_layout::TableIndex;
16use super::ot_layout_common::lookup_flags;
17use super::ot_layout_gpos_table::attach_type;
18use super::ot_layout_gsubgpos::{skipping_iterator_t, OT::hb_ot_apply_context_t};
19use super::ot_shape_plan::hb_ot_shape_plan_t;
20use super::{hb_font_t, hb_mask_t};
21use crate::U32Set;
22
23pub(crate) fn get_class(machine: &aat::StateTable, glyph_id: GlyphId, cache: &ClassCache) -> u8 {
24 if let Some(klass) = cache.get(glyph_id.to_u32()) {
25 return klass as u8;
26 }
27 let klass = machine
28 .class(GlyphId16::new(glyph_id.to_u32() as u16))
29 .unwrap_or(aat::class::OUT_OF_BOUNDS);
30 cache.set(glyph_id.to_u32(), klass as u32);
31 klass
32}
33
34pub fn hb_ot_layout_kern(
35 plan: &hb_ot_shape_plan_t,
36 face: &hb_font_t,
37 scale: Scale,
38 buffer: &mut hb_buffer_t,
39) -> Option<()> {
40 let mut c = AatApplyContext::new(plan, face, scale, buffer);
41
42 let (kern, subtable_caches) = c.face.aat_tables.kern.as_ref()?;
43
44 let mut subtable_idx = 0;
45
46 let mut seen_cross_stream = false;
47 for subtable in kern.subtables() {
48 let Ok(subtable) = subtable else { continue };
49
50 let subtable_cache = subtable_caches.get(subtable_idx);
51 let Some(subtable_cache) = subtable_cache.as_ref() else {
52 break;
53 };
54 subtable_idx += 1;
55
56 if subtable.is_variable() {
57 continue;
58 }
59
60 if c.buffer.direction.is_horizontal() != subtable.is_horizontal() {
61 continue;
62 }
63
64 c.first_set = Some(&subtable_cache.first_set);
65 c.second_set = Some(&subtable_cache.second_set);
66 c.machine_class_cache = Some(&subtable_cache.class_cache);
67 c.start_end_safe_to_break = subtable_cache.start_end_safe_to_break;
68
69 if !c.buffer_intersects_machine() {
70 continue;
71 }
72
73 let reverse = c.buffer.direction.is_backward();
74 let is_cross_stream = subtable.is_cross_stream();
75
76 if !seen_cross_stream && is_cross_stream {
77 seen_cross_stream = true;
78
79 for pos in &mut c.buffer.pos {
81 pos.set_attach_type(attach_type::CURSIVE);
82 pos.set_attach_chain(if c.buffer.direction.is_forward() {
83 -1
84 } else {
85 1
86 });
87 }
91 }
92
93 let Ok(kind) = subtable.kind() else {
94 continue;
95 };
96
97 if reverse != c.buffer_is_reversed {
98 c.reverse_buffer();
99 }
100
101 match kind {
102 SubtableKind::Format0(format0) if plan.requested_kerning => {
103 apply_simple_kerning(&mut c, &format0, is_cross_stream);
104 }
105 SubtableKind::Format1(format1) => {
106 apply_state_machine_kerning(&mut c, &format1, is_cross_stream);
107 }
108 SubtableKind::Format2(format2) if plan.requested_kerning => {
109 apply_simple_kerning(&mut c, &format2, is_cross_stream);
110 }
111 SubtableKind::Format3(format3) if plan.requested_kerning => {
112 apply_simple_kerning(&mut c, &format3, is_cross_stream);
113 }
114 _ => {}
115 }
116 }
117 if c.buffer_is_reversed {
118 c.reverse_buffer();
119 }
120 Some(())
121}
122
123fn machine_kern<F>(
124 face: &hb_font_t,
125 scale: Scale,
126 buffer: &mut hb_buffer_t,
127 kern_mask: hb_mask_t,
128 cross_stream: bool,
129 get_kerning: F,
130) where
131 F: Fn(u32, u32) -> i32,
132{
133 buffer.unsafe_to_concat(None, None);
134 let mut ctx = hb_ot_apply_context_t::new(TableIndex::GPOS, face, scale, buffer);
135 ctx.set_lookup_mask(kern_mask);
136 ctx.lookup_props = u32::from(lookup_flags::IGNORE_MARKS);
137 ctx.update_matchers();
138
139 let horizontal = ctx.buffer.direction.is_horizontal();
140 let use_x_scale = horizontal ^ cross_stream;
141 let mut i = 0;
142 let mut iter = skipping_iterator_t::new(&mut ctx, false);
143 while i < iter.buffer.len {
144 if (iter.buffer.info[i].mask & kern_mask) == 0 {
145 i += 1;
146 continue;
147 }
148
149 iter.reset_fast(i);
150
151 let mut unsafe_to = 0;
152 if !iter.next(Some(&mut unsafe_to)) {
153 i += 1;
154 continue;
155 }
156
157 let j = iter.index();
158
159 let info = &iter.buffer.info;
160 let kern = get_kerning(info[i].glyph_id, info[j].glyph_id);
161 let kern = if use_x_scale {
162 scale.scale_x(kern)
163 } else {
164 scale.scale_y(kern)
165 };
166 let pos = &mut iter.buffer.pos;
167 if kern != 0 {
168 if horizontal {
169 if cross_stream {
170 pos[j].y_offset = kern;
171 iter.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
172 } else {
173 let kern1 = kern >> 1;
174 let kern2 = kern - kern1;
175 pos[i].x_advance = pos[i].x_advance.saturating_add(kern1);
176 pos[j].x_advance = pos[j].x_advance.saturating_add(kern2);
177 pos[j].x_offset = pos[j].x_offset.saturating_add(kern2);
178 }
179 } else {
180 if cross_stream {
181 pos[j].x_offset = kern;
182 iter.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
183 } else {
184 let kern1 = kern >> 1;
185 let kern2 = kern - kern1;
186 pos[i].y_advance = pos[i].y_advance.saturating_add(kern1);
187 pos[j].y_advance = pos[j].y_advance.saturating_add(kern2);
188 pos[j].y_offset = pos[j].y_offset.saturating_add(kern2);
189 }
190 }
191
192 iter.buffer.unsafe_to_break(Some(i), Some(j + 1));
193 }
194
195 i = j;
196 }
197}
198
199fn apply_simple_kerning<T: SimpleKerning>(
200 c: &mut AatApplyContext,
201 subtable: &T,
202 is_cross_stream: bool,
203) {
204 let first_set = c.first_set.as_ref().unwrap();
205 let second_set = c.second_set.as_ref().unwrap();
206
207 machine_kern(
208 c.face,
209 c.scale,
210 c.buffer,
211 c.plan.kern_mask,
212 is_cross_stream,
213 |left, right| {
214 if !first_set.contains(left) || !second_set.contains(right) {
215 0
216 } else {
217 subtable
218 .simple_kerning(left.into(), right.into())
219 .unwrap_or(0)
220 }
221 },
222 );
223}
224
225struct StateMachineDriver {
226 stack: [usize; 8],
227 depth: usize,
228}
229
230pub trait CollectGlyphs {
231 fn collect_glyphs_filtered<F>(&self, _set: &mut U32Set, _num_glyphs: u32, _filter: F)
234 where
235 F: Fn(u8) -> bool;
236}
237
238impl CollectGlyphs for aat::ClassSubtable<'_> {
239 fn collect_glyphs_filtered<F>(&self, set: &mut U32Set, _num_glyphs: u32, filter: F)
240 where
241 F: Fn(u8) -> bool,
242 {
243 let first_glyph = self.first_glyph() as u32;
244 let class_array = self.class_array();
245 for (i, class) in class_array.iter().enumerate() {
246 let gid = first_glyph + i as u32;
247 if filter(*class) {
248 set.insert(gid);
249 }
250 }
251 }
252}
253
254fn collect_initial_glyphs(machine: &aat::StateTable, glyphs: &mut U32Set, num_glyphs: u32) {
255 let mut classes = U32Set::default();
256
257 let class_table = machine.header.class_table().ok();
258 let Some(class_table) = class_table else {
259 return;
260 };
261
262 let n_classes = machine.header.state_size();
263 for i in 0..n_classes {
264 if let Ok(entry) = machine.entry(START_OF_TEXT, i as u8) {
265 if entry.new_state == START_OF_TEXT
266 && !entry.is_action_initiable()
267 && !entry.is_actionable()
268 {
269 continue;
270 }
271 classes.insert(i as u32);
272 }
273 }
274
275 let filter = |class: u8| classes.contains(class as u32);
278
279 if filter(aat::class::DELETED_GLYPH) {
280 glyphs.insert(DELETED_GLYPH);
281 }
282
283 class_table.collect_glyphs_filtered(glyphs, num_glyphs, filter);
284}
285
286fn collect_start_end_safe_to_break(machine: &aat::StateTable) -> u64 {
287 let mut result = 0u64;
288 for state in 0..64 {
289 let bit = if let Ok(entry) = machine.entry(state, aat::class::END_OF_TEXT) {
290 !entry.is_actionable()
291 } else {
292 true
293 };
294 if bit {
295 result |= 1 << state;
296 }
297 }
298 result
299}
300
301fn apply_state_machine_kerning(
302 c: &mut AatApplyContext,
303 subtable: &aat::StateTable,
304 is_cross_stream: bool,
305) {
306 let mut driver = StateMachineDriver {
307 stack: [0; 8],
308 depth: 0,
309 };
310
311 let mut state = START_OF_TEXT;
312 c.buffer.idx = 0;
313 loop {
314 let class = if c.buffer.idx < c.buffer.len {
315 get_class(
316 subtable,
317 c.buffer.cur(0).as_glyph(),
318 c.machine_class_cache.unwrap(),
319 )
320 } else {
321 aat::class::END_OF_TEXT
322 };
323
324 let Ok(entry) = subtable.entry(state, class) else {
325 break;
326 };
327
328 let next_state = entry.new_state;
329
330 let is_safe_to_break =
359 !entry.is_actionable() &&
361
362 (
364 state == START_OF_TEXT
365 || (!entry.has_advance() && next_state == START_OF_TEXT)
366 ||
367 {
368 if let Ok(wouldbe_entry) = subtable.entry(START_OF_TEXT, class) {
370 !wouldbe_entry.is_actionable() &&
372
373 (
375 next_state == wouldbe_entry.new_state &&
376 entry.has_advance() == wouldbe_entry.has_advance()
377 )
378 } else {
379 false
380 }
381 }
382 ) &&
383
384 (
386 if state < 64 {
387 (c.start_end_safe_to_break & (1 << state)) != 0
388 } else {
389 if let Ok(end_entry) = subtable.entry(state, aat::class::END_OF_TEXT) {
390 !end_entry.is_actionable()
391 } else {
392 false
393 }
394 }
395 )
396 ;
397
398 if !is_safe_to_break && c.buffer.backtrack_len() > 0 && c.buffer.idx < c.buffer.len {
399 c.buffer.unsafe_to_break_from_outbuffer(
400 Some(c.buffer.backtrack_len() - 1),
401 Some(c.buffer.idx + 1),
402 );
403 }
404
405 state_machine_transition(c, subtable, &entry, is_cross_stream, &mut driver);
406
407 state = next_state;
408
409 if c.buffer.idx >= c.buffer.len {
410 break;
411 }
412
413 c.buffer.max_ops -= 1;
414 if entry.has_advance() || c.buffer.max_ops <= 0 {
415 c.buffer.next_glyph();
416 }
417 }
418}
419
420#[inline(always)]
421fn state_machine_transition(
422 c: &mut AatApplyContext,
423 subtable: &aat::StateTable,
424 entry: &aat::StateEntry,
425 is_cross_stream: bool,
426 driver: &mut StateMachineDriver,
427) {
428 let scale = c.scale;
429 let use_x_scale = c.buffer.direction.is_horizontal() ^ is_cross_stream;
430 let buffer = &mut *c.buffer;
431 let kern_mask = c.plan.kern_mask;
432
433 if entry.has_push() {
434 if driver.depth < driver.stack.len() {
435 driver.stack[driver.depth] = buffer.idx;
436 driver.depth += 1;
437 } else {
438 driver.depth = 0; }
440 }
441
442 if entry.has_offset() && driver.depth != 0 {
443 let mut value_offset = entry.value_offset();
444 let Ok(mut value) = subtable.read_value::<i16>(value_offset as usize) else {
445 driver.depth = 0;
446 return;
447 };
448
449 let mut last = false;
453 while !last && driver.depth != 0 {
454 driver.depth -= 1;
455 let idx = driver.stack[driver.depth];
456 let mut v = value as i32;
457 value_offset = value_offset.wrapping_add(2);
458 value = subtable
459 .read_value::<i16>(value_offset as usize)
460 .unwrap_or(0);
461 if idx >= buffer.len {
462 continue;
463 }
464
465 last = v & 1 != 0;
467 v &= !1;
468 let scaled_v = if use_x_scale {
469 scale.scale_x(v)
470 } else {
471 scale.scale_y(v)
472 };
473
474 let mut has_gpos_attachment = false;
479 let glyph_mask = buffer.info[idx].mask;
480 let pos = &mut buffer.pos[idx];
481
482 if buffer.direction.is_horizontal() {
483 if is_cross_stream {
484 if v == -0x8000 {
487 pos.set_attach_type(0);
488 pos.set_attach_chain(0);
489 pos.y_offset = 0;
490 } else if pos.attach_type() != 0 {
491 pos.y_offset = pos.y_offset.saturating_add(scaled_v);
492 has_gpos_attachment = true;
493 }
494 } else if glyph_mask & kern_mask != 0 {
495 pos.x_advance = pos.x_advance.saturating_add(scaled_v);
496 pos.x_offset = pos.x_offset.saturating_add(scaled_v);
497 }
498 } else {
499 if is_cross_stream {
500 if v == -0x8000 {
502 pos.set_attach_type(0);
503 pos.set_attach_chain(0);
504 pos.x_offset = 0;
505 } else if pos.attach_type() != 0 {
506 pos.x_offset = pos.x_offset.saturating_add(scaled_v);
507 has_gpos_attachment = true;
508 }
509 } else if glyph_mask & kern_mask != 0 {
510 if pos.y_offset == 0 {
511 pos.y_advance = pos.y_advance.saturating_add(scaled_v);
512 pos.y_offset = pos.y_offset.saturating_add(scaled_v);
513 }
514 }
515 }
516
517 if has_gpos_attachment {
518 buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
519 }
520 }
521 }
522}
523
524trait KernStateEntryExt {
525 fn flags(&self) -> u16;
526
527 fn is_action_initiable(&self) -> bool {
528 self.flags() & 0x8000 != 0
529 }
530
531 fn is_actionable(&self) -> bool {
532 self.flags() & 0x3FFF != 0
533 }
534
535 fn has_offset(&self) -> bool {
536 self.flags() & 0x3FFF != 0
537 }
538
539 fn value_offset(&self) -> u16 {
540 self.flags() & 0x3FFF
541 }
542
543 fn has_advance(&self) -> bool {
544 self.flags() & 0x4000 == 0
545 }
546
547 fn has_push(&self) -> bool {
548 self.flags() & 0x8000 != 0
549 }
550}
551
552impl<T> KernStateEntryExt for aat::StateEntry<T> {
553 fn flags(&self) -> u16 {
554 self.flags
555 }
556}
557
558impl SimpleKerning for Subtable0<'_> {
559 fn simple_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
560 self.kerning(left, right)
561 }
562 fn collect_glyphs(&self, first_set: &mut U32Set, second_set: &mut U32Set, _num_glyphs: u32) {
563 for &pair in self.pairs() {
564 first_set.insert(pair.left.get().to_u32());
565 second_set.insert(pair.right.get().to_u32());
566 }
567 }
568}
569
570impl SimpleKerning for Subtable2<'_> {
571 fn simple_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
572 self.kerning(left, right)
573 }
574 fn collect_glyphs(&self, first_set: &mut U32Set, second_set: &mut U32Set, _num_glyphs: u32) {
575 let left_classes = &self.left_offset_table;
576 let right_classes = &self.right_offset_table;
577
578 let first_glyph = left_classes.first_glyph().to_u32();
579 let last_glyphs = first_glyph + left_classes.n_glyphs().saturating_sub(1) as u32;
580 first_set.insert_range(first_glyph..=last_glyphs);
581
582 let first_glyph = right_classes.first_glyph().to_u32();
583 let last_glyphs = first_glyph + right_classes.n_glyphs().saturating_sub(1) as u32;
584 second_set.insert_range(first_glyph..=last_glyphs);
585 }
586}
587
588impl SimpleKerning for Subtable3<'_> {
589 fn simple_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
590 self.kerning(left, right)
591 }
592 fn collect_glyphs(&self, first_set: &mut U32Set, second_set: &mut U32Set, _num_glyphs: u32) {
593 first_set.insert_range(0..=self.glyph_count().saturating_sub(1) as u32);
594 second_set.insert_range(0..=self.glyph_count().saturating_sub(1) as u32);
595 }
596}
597
598pub(crate) struct KernSubtableCache {
599 start_end_safe_to_break: u64,
600 first_set: U32Set,
601 second_set: U32Set,
602 class_cache: Box<ClassCache>,
603}
604
605impl KernSubtableCache {
606 pub(crate) fn new(subtable: &Subtable, num_glyphs: u32) -> Self {
607 let mut start_end_safe_to_break = 0u64;
608 let mut first_set = U32Set::default();
609 let mut second_set = U32Set::default();
610 if let Ok(kind) = subtable.kind() {
611 match &kind {
612 SubtableKind::Format0(format0) => {
613 format0.collect_glyphs(&mut first_set, &mut second_set, num_glyphs);
614 }
615 SubtableKind::Format1(format1) => {
616 start_end_safe_to_break = collect_start_end_safe_to_break(format1);
617 collect_initial_glyphs(format1, &mut first_set, num_glyphs);
618 }
619 SubtableKind::Format2(format2) => {
620 format2.collect_glyphs(&mut first_set, &mut second_set, num_glyphs);
621 }
622 SubtableKind::Format3(format3) => {
623 format3.collect_glyphs(&mut first_set, &mut second_set, num_glyphs);
624 }
625 }
626 }
627 KernSubtableCache {
628 start_end_safe_to_break,
629 first_set,
630 second_set,
631 class_cache: Box::new(ClassCache::new()),
632 }
633 }
634}