1 | /* |
2 | * Copyright © 2010,2012 Google, Inc. |
3 | * |
4 | * This is part of HarfBuzz, a text shaping library. |
5 | * |
6 | * Permission is hereby granted, without written agreement and without |
7 | * license or royalty fees, to use, copy, modify, and distribute this |
8 | * software and its documentation for any purpose, provided that the |
9 | * above copyright notice and the following two paragraphs appear in |
10 | * all copies of this software. |
11 | * |
12 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | * DAMAGE. |
17 | * |
18 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | * |
24 | * Google Author(s): Behdad Esfahbod |
25 | */ |
26 | |
27 | #include "hb.hh" |
28 | |
29 | #ifndef HB_NO_OT_SHAPE |
30 | |
31 | #include "hb-ot-shaper.hh" |
32 | |
33 | |
34 | static bool |
35 | compose_hebrew (const hb_ot_shape_normalize_context_t *c, |
36 | hb_codepoint_t a, |
37 | hb_codepoint_t b, |
38 | hb_codepoint_t *ab) |
39 | { |
40 | /* Hebrew presentation-form shaping. |
41 | * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 |
42 | * Hebrew presentation forms with dagesh, for characters U+05D0..05EA; |
43 | * Note that some letters do not have a dagesh presForm encoded. |
44 | */ |
45 | static const hb_codepoint_t sDageshForms[0x05EAu - 0x05D0u + 1] = { |
46 | 0xFB30u, /* ALEF */ |
47 | 0xFB31u, /* BET */ |
48 | 0xFB32u, /* GIMEL */ |
49 | 0xFB33u, /* DALET */ |
50 | 0xFB34u, /* HE */ |
51 | 0xFB35u, /* VAV */ |
52 | 0xFB36u, /* ZAYIN */ |
53 | 0x0000u, /* HET */ |
54 | 0xFB38u, /* TET */ |
55 | 0xFB39u, /* YOD */ |
56 | 0xFB3Au, /* FINAL KAF */ |
57 | 0xFB3Bu, /* KAF */ |
58 | 0xFB3Cu, /* LAMED */ |
59 | 0x0000u, /* FINAL MEM */ |
60 | 0xFB3Eu, /* MEM */ |
61 | 0x0000u, /* FINAL NUN */ |
62 | 0xFB40u, /* NUN */ |
63 | 0xFB41u, /* SAMEKH */ |
64 | 0x0000u, /* AYIN */ |
65 | 0xFB43u, /* FINAL PE */ |
66 | 0xFB44u, /* PE */ |
67 | 0x0000u, /* FINAL TSADI */ |
68 | 0xFB46u, /* TSADI */ |
69 | 0xFB47u, /* QOF */ |
70 | 0xFB48u, /* RESH */ |
71 | 0xFB49u, /* SHIN */ |
72 | 0xFB4Au /* TAV */ |
73 | }; |
74 | |
75 | bool found = (bool) c->unicode->compose (a, b, ab); |
76 | |
77 | #ifdef HB_NO_OT_SHAPER_HEBREW_FALLBACK |
78 | return found; |
79 | #endif |
80 | |
81 | if (!found && !c->plan->has_gpos_mark) |
82 | { |
83 | /* Special-case Hebrew presentation forms that are excluded from |
84 | * standard normalization, but wanted for old fonts. */ |
85 | switch (b) { |
86 | case 0x05B4u: /* HIRIQ */ |
87 | if (a == 0x05D9u) { /* YOD */ |
88 | *ab = 0xFB1Du; |
89 | found = true; |
90 | } |
91 | break; |
92 | case 0x05B7u: /* PATAH */ |
93 | if (a == 0x05F2u) { /* YIDDISH YOD YOD */ |
94 | *ab = 0xFB1Fu; |
95 | found = true; |
96 | } else if (a == 0x05D0u) { /* ALEF */ |
97 | *ab = 0xFB2Eu; |
98 | found = true; |
99 | } |
100 | break; |
101 | case 0x05B8u: /* QAMATS */ |
102 | if (a == 0x05D0u) { /* ALEF */ |
103 | *ab = 0xFB2Fu; |
104 | found = true; |
105 | } |
106 | break; |
107 | case 0x05B9u: /* HOLAM */ |
108 | if (a == 0x05D5u) { /* VAV */ |
109 | *ab = 0xFB4Bu; |
110 | found = true; |
111 | } |
112 | break; |
113 | case 0x05BCu: /* DAGESH */ |
114 | if (a >= 0x05D0u && a <= 0x05EAu) { |
115 | *ab = sDageshForms[a - 0x05D0u]; |
116 | found = (*ab != 0); |
117 | } else if (a == 0xFB2Au) { /* SHIN WITH SHIN DOT */ |
118 | *ab = 0xFB2Cu; |
119 | found = true; |
120 | } else if (a == 0xFB2Bu) { /* SHIN WITH SIN DOT */ |
121 | *ab = 0xFB2Du; |
122 | found = true; |
123 | } |
124 | break; |
125 | case 0x05BFu: /* RAFE */ |
126 | switch (a) { |
127 | case 0x05D1u: /* BET */ |
128 | *ab = 0xFB4Cu; |
129 | found = true; |
130 | break; |
131 | case 0x05DBu: /* KAF */ |
132 | *ab = 0xFB4Du; |
133 | found = true; |
134 | break; |
135 | case 0x05E4u: /* PE */ |
136 | *ab = 0xFB4Eu; |
137 | found = true; |
138 | break; |
139 | } |
140 | break; |
141 | case 0x05C1u: /* SHIN DOT */ |
142 | if (a == 0x05E9u) { /* SHIN */ |
143 | *ab = 0xFB2Au; |
144 | found = true; |
145 | } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ |
146 | *ab = 0xFB2Cu; |
147 | found = true; |
148 | } |
149 | break; |
150 | case 0x05C2u: /* SIN DOT */ |
151 | if (a == 0x05E9u) { /* SHIN */ |
152 | *ab = 0xFB2Bu; |
153 | found = true; |
154 | } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ |
155 | *ab = 0xFB2Du; |
156 | found = true; |
157 | } |
158 | break; |
159 | } |
160 | } |
161 | |
162 | return found; |
163 | } |
164 | |
165 | static void |
166 | reorder_marks_hebrew (const hb_ot_shape_plan_t *plan HB_UNUSED, |
167 | hb_buffer_t *buffer, |
168 | unsigned int start, |
169 | unsigned int end) |
170 | { |
171 | hb_glyph_info_t *info = buffer->info; |
172 | |
173 | for (unsigned i = start + 2; i < end; i++) |
174 | { |
175 | unsigned c0 = info_cc (info[i - 2]); |
176 | unsigned c1 = info_cc (info[i - 1]); |
177 | unsigned c2 = info_cc (info[i - 0]); |
178 | |
179 | if ((c0 == HB_MODIFIED_COMBINING_CLASS_CCC17 || c0 == HB_MODIFIED_COMBINING_CLASS_CCC18) /* patach or qamats */ && |
180 | (c1 == HB_MODIFIED_COMBINING_CLASS_CCC10 || c1 == HB_MODIFIED_COMBINING_CLASS_CCC14) /* sheva or hiriq */ && |
181 | (c2 == HB_MODIFIED_COMBINING_CLASS_CCC22 || c2 == HB_UNICODE_COMBINING_CLASS_BELOW) /* meteg or below */) |
182 | { |
183 | buffer->merge_clusters (i - 1, i + 1); |
184 | hb_swap (info[i - 1], info[i]); |
185 | break; |
186 | } |
187 | } |
188 | |
189 | |
190 | } |
191 | |
192 | const hb_ot_shaper_t _hb_ot_shaper_hebrew = |
193 | { |
194 | nullptr, /* collect_features */ |
195 | nullptr, /* override_features */ |
196 | nullptr, /* data_create */ |
197 | nullptr, /* data_destroy */ |
198 | nullptr, /* preprocess_text */ |
199 | nullptr, /* postprocess_glyphs */ |
200 | nullptr, /* decompose */ |
201 | compose_hebrew, |
202 | nullptr, /* setup_masks */ |
203 | reorder_marks_hebrew, |
204 | HB_TAG ('h','e','b','r'), /* gpos_tag. https://github.com/harfbuzz/harfbuzz/issues/347#issuecomment-267838368 */ |
205 | HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, |
206 | HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, |
207 | true, /* fallback_position */ |
208 | }; |
209 | |
210 | |
211 | #endif |
212 | |