Skip to main content

harfrust/hb/
ot_shaper_hebrew.rs

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