1use crate::buffer::{Buffer, BufferFlags};
2use crate::{Face, GlyphInfo};
3
4pub fn insert_dotted_circles(
5 face: &Face,
6 buffer: &mut Buffer,
7 broken_syllable_type: u8,
8 dottedcircle_category: u8,
9 repha_category: Option<u8>,
10 dottedcircle_position: Option<u8>,
11) {
12 if buffer
13 .flags
14 .contains(BufferFlags::DO_NOT_INSERT_DOTTED_CIRCLE)
15 {
16 return;
17 }
18
19 // Note: This loop is extra overhead, but should not be measurable.
20 // TODO Use a buffer scratch flag to remove the loop.
21 let has_broken_syllables = buffer
22 .info_slice()
23 .iter()
24 .any(|info| info.syllable() & 0x0F == broken_syllable_type);
25
26 if !has_broken_syllables {
27 return;
28 }
29
30 let dottedcircle_glyph = match face.glyph_index(0x25CC) {
31 Some(g) => g.0 as u32,
32 None => return,
33 };
34
35 let mut dottedcircle = GlyphInfo {
36 glyph_id: 0x25CC,
37 ..GlyphInfo::default()
38 };
39 dottedcircle.set_complex_var_u8_category(dottedcircle_category);
40 if let Some(dottedcircle_position) = dottedcircle_position {
41 dottedcircle.set_complex_var_u8_auxiliary(dottedcircle_position);
42 }
43 dottedcircle.glyph_id = dottedcircle_glyph;
44
45 buffer.clear_output();
46
47 buffer.idx = 0;
48 let mut last_syllable = 0;
49 while buffer.idx < buffer.len {
50 let syllable = buffer.cur(0).syllable();
51 if last_syllable != syllable && (syllable & 0x0F) == broken_syllable_type {
52 last_syllable = syllable;
53
54 let mut ginfo = dottedcircle;
55 ginfo.cluster = buffer.cur(0).cluster;
56 ginfo.mask = buffer.cur(0).mask;
57 ginfo.set_syllable(buffer.cur(0).syllable());
58
59 // Insert dottedcircle after possible Repha.
60 if let Some(repha_category) = repha_category {
61 while buffer.idx < buffer.len
62 && last_syllable == buffer.cur(0).syllable()
63 && buffer.cur(0).complex_var_u8_category() == repha_category
64 {
65 buffer.next_glyph();
66 }
67 }
68
69 buffer.output_info(ginfo);
70 } else {
71 buffer.next_glyph();
72 }
73 }
74
75 buffer.sync();
76}
77