1 | use super::ot_shape_normalize::*; |
2 | use super::ot_shaper::*; |
3 | use super::{hb_tag_t, unicode}; |
4 | use crate::hb::buffer::hb_buffer_t; |
5 | use crate::hb::ot_layout::_hb_glyph_info_get_modified_combining_class; |
6 | use crate::hb::ot_shape_plan::hb_ot_shape_plan_t; |
7 | use crate::hb::unicode::modified_combining_class; |
8 | use unicode_ccc::CanonicalCombiningClass; |
9 | |
10 | pub const HEBREW_SHAPER: hb_ot_shaper_t = hb_ot_shaper_t { |
11 | collect_features: None, |
12 | override_features: None, |
13 | create_data: None, |
14 | preprocess_text: None, |
15 | postprocess_glyphs: None, |
16 | normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, |
17 | decompose: None, |
18 | compose: Some(compose), |
19 | setup_masks: None, |
20 | gpos_tag: Some(hb_tag_t::from_bytes(b"hebr" )), |
21 | reorder_marks: Some(reorder_marks_hebrew), |
22 | zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, |
23 | fallback_position: true, |
24 | }; |
25 | |
26 | fn reorder_marks_hebrew( |
27 | _: &hb_ot_shape_plan_t, |
28 | buffer: &mut hb_buffer_t, |
29 | start: usize, |
30 | end: usize, |
31 | ) { |
32 | for i: usize in start + 2..end { |
33 | let c0: hb_glyph_info_t = buffer.info[i - 2]; |
34 | let c1: hb_glyph_info_t = buffer.info[i - 1]; |
35 | let c2: hb_glyph_info_t = buffer.info[i - 0]; |
36 | |
37 | if (_hb_glyph_info_get_modified_combining_class(&c0) == modified_combining_class::CCC17 |
38 | || _hb_glyph_info_get_modified_combining_class(&c0) == modified_combining_class::CCC18) /* patach or qamats */ |
39 | && |
40 | (_hb_glyph_info_get_modified_combining_class(&c1) == modified_combining_class::CCC10 |
41 | || _hb_glyph_info_get_modified_combining_class(&c1) == modified_combining_class::CCC14) /* sheva or hiriq */ && |
42 | (_hb_glyph_info_get_modified_combining_class(&c2) == modified_combining_class::CCC22 |
43 | || _hb_glyph_info_get_modified_combining_class(&c2) == CanonicalCombiningClass::Below as u8) |
44 | /* meteg or below */ |
45 | { |
46 | buffer.merge_clusters(start:i - 1, end:i + 1); |
47 | buffer.info.swap(a:i - 1, b:i); |
48 | break; |
49 | } |
50 | } |
51 | } |
52 | |
53 | const S_DAGESH_FORMS: &[char] = &[ |
54 | ' \u{FB30}' , // ALEF |
55 | ' \u{FB31}' , // BET |
56 | ' \u{FB32}' , // GIMEL |
57 | ' \u{FB33}' , // DALET |
58 | ' \u{FB34}' , // HE |
59 | ' \u{FB35}' , // VAV |
60 | ' \u{FB36}' , // ZAYIN |
61 | ' \u{0000}' , // HET |
62 | ' \u{FB38}' , // TET |
63 | ' \u{FB39}' , // YOD |
64 | ' \u{FB3A}' , // FINAL KAF |
65 | ' \u{FB3B}' , // KAF |
66 | ' \u{FB3C}' , // LAMED |
67 | ' \u{0000}' , // FINAL MEM |
68 | ' \u{FB3E}' , // MEM |
69 | ' \u{0000}' , // FINAL NUN |
70 | ' \u{FB40}' , // NUN |
71 | ' \u{FB41}' , // SAMEKH |
72 | ' \u{0000}' , // AYIN |
73 | ' \u{FB43}' , // FINAL PE |
74 | ' \u{FB44}' , // PE |
75 | ' \u{0000}' , // FINAL TSADI |
76 | ' \u{FB46}' , // TSADI |
77 | ' \u{FB47}' , // QOF |
78 | ' \u{FB48}' , // RESH |
79 | ' \u{FB49}' , // SHIN |
80 | ' \u{FB4A}' , // TAV |
81 | ]; |
82 | |
83 | fn compose(ctx: &hb_ot_shape_normalize_context_t, a: char, b: char) -> Option<char> { |
84 | // Hebrew presentation-form shaping. |
85 | // https://bugzilla.mozilla.org/show_bug.cgi?id=728866 |
86 | // Hebrew presentation forms with dagesh, for characters U+05D0..05EA; |
87 | // Note that some letters do not have a dagesh presForm encoded. |
88 | match unicode::compose(a, b) { |
89 | Some(c) => Some(c), |
90 | None if !ctx.plan.has_gpos_mark => { |
91 | // Special-case Hebrew presentation forms that are excluded from |
92 | // standard normalization, but wanted for old fonts. |
93 | let a = a as u32; |
94 | let b = b as u32; |
95 | match b { |
96 | 0x05B4 => { |
97 | // HIRIQ |
98 | match a { |
99 | 0x05D9 => Some(' \u{FB1D}' ), // YOD |
100 | _ => None, |
101 | } |
102 | } |
103 | 0x05B7 => { |
104 | // PATAH |
105 | match a { |
106 | 0x05D9 => Some(' \u{FB1F}' ), // YIDDISH YOD YOD |
107 | 0x05D0 => Some(' \u{FB2E}' ), // ALEF |
108 | _ => None, |
109 | } |
110 | } |
111 | 0x05B8 => { |
112 | // QAMATS |
113 | match a { |
114 | 0x05D0 => Some(' \u{FB2F}' ), // ALEF |
115 | _ => None, |
116 | } |
117 | } |
118 | 0x05B9 => { |
119 | // HOLAM |
120 | match a { |
121 | 0x05D5 => Some(' \u{FB4B}' ), // VAV |
122 | _ => None, |
123 | } |
124 | } |
125 | 0x05BC => { |
126 | // DAGESH |
127 | match a { |
128 | 0x05D0..=0x05EA => { |
129 | let c = S_DAGESH_FORMS[a as usize - 0x05D0]; |
130 | if c != ' \0' { |
131 | Some(c) |
132 | } else { |
133 | None |
134 | } |
135 | } |
136 | 0xFB2A => Some(' \u{FB2C}' ), // SHIN WITH SHIN DOT |
137 | 0xFB2B => Some(' \u{FB2D}' ), // SHIN WITH SIN DOT |
138 | _ => None, |
139 | } |
140 | } |
141 | 0x05BF => { |
142 | // RAFE |
143 | match a { |
144 | 0x05D1 => Some(' \u{FB4C}' ), // BET |
145 | 0x05DB => Some(' \u{FB4D}' ), // KAF |
146 | 0x05E4 => Some(' \u{FB4E}' ), // PE |
147 | _ => None, |
148 | } |
149 | } |
150 | 0x05C1 => { |
151 | // SHIN DOT |
152 | match a { |
153 | 0x05E9 => Some(' \u{FB2A}' ), // SHIN |
154 | 0xFB49 => Some(' \u{FB2C}' ), // SHIN WITH DAGESH |
155 | _ => None, |
156 | } |
157 | } |
158 | 0x05C2 => { |
159 | // SIN DOT |
160 | match a { |
161 | 0x05E9 => Some(' \u{FB2B}' ), // SHIN |
162 | 0xFB49 => Some(' \u{FB2D}' ), // SHIN WITH DAGESH |
163 | _ => None, |
164 | } |
165 | } |
166 | _ => None, |
167 | } |
168 | } |
169 | None => None, |
170 | } |
171 | } |
172 | |