|
1 /* |
|
2 * Copyright © 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 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH |
|
28 #define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH |
|
29 |
|
30 #include "hb-private.hh" |
|
31 |
|
32 #include "hb-ot-shape-private.hh" |
|
33 #include "hb-ot-layout-gsub-table.hh" |
|
34 |
|
35 |
|
36 /* Features ordered the same as the entries in shaping_table rows, |
|
37 * followed by rlig. Don't change. */ |
|
38 static const hb_tag_t arabic_fallback_features[] = |
|
39 { |
|
40 HB_TAG('i','n','i','t'), |
|
41 HB_TAG('m','e','d','i'), |
|
42 HB_TAG('f','i','n','a'), |
|
43 HB_TAG('i','s','o','l'), |
|
44 HB_TAG('r','l','i','g'), |
|
45 }; |
|
46 |
|
47 static OT::SubstLookup * |
|
48 arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED, |
|
49 hb_font_t *font, |
|
50 unsigned int feature_index) |
|
51 { |
|
52 OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; |
|
53 OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; |
|
54 unsigned int num_glyphs = 0; |
|
55 |
|
56 /* Populate arrays */ |
|
57 for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++) |
|
58 { |
|
59 hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index]; |
|
60 hb_codepoint_t u_glyph, s_glyph; |
|
61 |
|
62 if (!s || |
|
63 !hb_font_get_glyph (font, u, 0, &u_glyph) || |
|
64 !hb_font_get_glyph (font, s, 0, &s_glyph) || |
|
65 u_glyph == s_glyph || |
|
66 u_glyph > 0xFFFFu || s_glyph > 0xFFFFu) |
|
67 continue; |
|
68 |
|
69 glyphs[num_glyphs].set (u_glyph); |
|
70 substitutes[num_glyphs].set (s_glyph); |
|
71 |
|
72 num_glyphs++; |
|
73 } |
|
74 |
|
75 if (!num_glyphs) |
|
76 return NULL; |
|
77 |
|
78 /* Bubble-sort or something equally good! |
|
79 * May not be good-enough for presidential candidate interviews, but good-enough for us... */ |
|
80 hb_stable_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]); |
|
81 |
|
82 OT::Supplier<OT::GlyphID> glyphs_supplier (glyphs, num_glyphs); |
|
83 OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs); |
|
84 |
|
85 /* Each glyph takes four bytes max, and there's some overhead. */ |
|
86 char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128]; |
|
87 OT::hb_serialize_context_t c (buf, sizeof (buf)); |
|
88 OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); |
|
89 bool ret = lookup->serialize_single (&c, |
|
90 OT::LookupFlag::IgnoreMarks, |
|
91 glyphs_supplier, |
|
92 substitutes_supplier, |
|
93 num_glyphs); |
|
94 c.end_serialize (); |
|
95 /* TODO sanitize the results? */ |
|
96 |
|
97 return ret ? c.copy<OT::SubstLookup> () : NULL; |
|
98 } |
|
99 |
|
100 static OT::SubstLookup * |
|
101 arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED, |
|
102 hb_font_t *font) |
|
103 { |
|
104 OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; |
|
105 unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; |
|
106 unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)]; |
|
107 unsigned int num_first_glyphs = 0; |
|
108 |
|
109 /* We know that all our ligatures are 2-component */ |
|
110 OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; |
|
111 unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)]; |
|
112 OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */]; |
|
113 unsigned int num_ligatures = 0; |
|
114 |
|
115 /* Populate arrays */ |
|
116 |
|
117 /* Sort out the first-glyphs */ |
|
118 for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++) |
|
119 { |
|
120 hb_codepoint_t first_u = ligature_table[first_glyph_idx].first; |
|
121 hb_codepoint_t first_glyph; |
|
122 if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) |
|
123 continue; |
|
124 first_glyphs[num_first_glyphs].set (first_glyph); |
|
125 ligature_per_first_glyph_count_list[num_first_glyphs] = 0; |
|
126 first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; |
|
127 num_first_glyphs++; |
|
128 } |
|
129 hb_stable_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]); |
|
130 |
|
131 /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ |
|
132 for (unsigned int i = 0; i < num_first_glyphs; i++) |
|
133 { |
|
134 unsigned int first_glyph_idx = first_glyphs_indirection[i]; |
|
135 |
|
136 for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++) |
|
137 { |
|
138 hb_codepoint_t second_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second; |
|
139 hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature; |
|
140 hb_codepoint_t second_glyph, ligature_glyph; |
|
141 if (!second_u || |
|
142 !hb_font_get_glyph (font, second_u, 0, &second_glyph) || |
|
143 !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph)) |
|
144 continue; |
|
145 |
|
146 ligature_per_first_glyph_count_list[i]++; |
|
147 |
|
148 ligature_list[num_ligatures].set (ligature_glyph); |
|
149 component_count_list[num_ligatures] = 2; |
|
150 component_list[num_ligatures].set (second_glyph); |
|
151 num_ligatures++; |
|
152 } |
|
153 } |
|
154 |
|
155 if (!num_ligatures) |
|
156 return NULL; |
|
157 |
|
158 OT::Supplier<OT::GlyphID> first_glyphs_supplier (first_glyphs, num_first_glyphs); |
|
159 OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier (ligature_per_first_glyph_count_list, num_first_glyphs); |
|
160 OT::Supplier<OT::GlyphID> ligatures_supplier (ligature_list, num_ligatures); |
|
161 OT::Supplier<unsigned int > component_count_supplier (component_count_list, num_ligatures); |
|
162 OT::Supplier<OT::GlyphID> component_supplier (component_list, num_ligatures); |
|
163 |
|
164 /* 16 bytes per ligature ought to be enough... */ |
|
165 char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128]; |
|
166 OT::hb_serialize_context_t c (buf, sizeof (buf)); |
|
167 OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); |
|
168 bool ret = lookup->serialize_ligature (&c, |
|
169 OT::LookupFlag::IgnoreMarks, |
|
170 first_glyphs_supplier, |
|
171 ligature_per_first_glyph_count_supplier, |
|
172 num_first_glyphs, |
|
173 ligatures_supplier, |
|
174 component_count_supplier, |
|
175 component_supplier); |
|
176 |
|
177 c.end_serialize (); |
|
178 /* TODO sanitize the results? */ |
|
179 |
|
180 return ret ? c.copy<OT::SubstLookup> () : NULL; |
|
181 } |
|
182 |
|
183 static OT::SubstLookup * |
|
184 arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan, |
|
185 hb_font_t *font, |
|
186 unsigned int feature_index) |
|
187 { |
|
188 if (feature_index < 4) |
|
189 return arabic_fallback_synthesize_lookup_single (plan, font, feature_index); |
|
190 else |
|
191 return arabic_fallback_synthesize_lookup_ligature (plan, font); |
|
192 } |
|
193 |
|
194 #define ARABIC_FALLBACK_MAX_LOOKUPS 5 |
|
195 |
|
196 struct arabic_fallback_plan_t |
|
197 { |
|
198 ASSERT_POD (); |
|
199 |
|
200 unsigned int num_lookups; |
|
201 bool free_lookups; |
|
202 |
|
203 hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS]; |
|
204 OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS]; |
|
205 hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS]; |
|
206 }; |
|
207 |
|
208 static const arabic_fallback_plan_t arabic_fallback_plan_nil = {}; |
|
209 |
|
210 #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256) |
|
211 #define HB_WITH_WIN1256 |
|
212 #endif |
|
213 |
|
214 #ifdef HB_WITH_WIN1256 |
|
215 #include "hb-ot-shape-complex-arabic-win1256.hh" |
|
216 #endif |
|
217 |
|
218 struct ManifestLookup { |
|
219 OT::Tag tag; |
|
220 OT::OffsetTo<OT::SubstLookup> lookupOffset; |
|
221 }; |
|
222 typedef OT::ArrayOf<ManifestLookup> Manifest; |
|
223 |
|
224 static bool |
|
225 arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan, |
|
226 const hb_ot_shape_plan_t *plan, |
|
227 hb_font_t *font) |
|
228 { |
|
229 #ifdef HB_WITH_WIN1256 |
|
230 /* Does this font look like it's Windows-1256-encoded? */ |
|
231 hb_codepoint_t g; |
|
232 if (!(hb_font_get_glyph (font, 0x0627u, 0, &g) && g == 199 /* ALEF */ && |
|
233 hb_font_get_glyph (font, 0x0644u, 0, &g) && g == 225 /* LAM */ && |
|
234 hb_font_get_glyph (font, 0x0649u, 0, &g) && g == 236 /* ALEF MAKSURA */ && |
|
235 hb_font_get_glyph (font, 0x064Au, 0, &g) && g == 237 /* YEH */ && |
|
236 hb_font_get_glyph (font, 0x0652u, 0, &g) && g == 250 /* SUKUN */)) |
|
237 return false; |
|
238 |
|
239 const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest); |
|
240 ASSERT_STATIC (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup) |
|
241 <= ARABIC_FALLBACK_MAX_LOOKUPS); |
|
242 /* TODO sanitize the table? */ |
|
243 |
|
244 unsigned j = 0; |
|
245 unsigned int count = manifest.len; |
|
246 for (unsigned int i = 0; i < count; i++) |
|
247 { |
|
248 fallback_plan->mask_array[j] = plan->map.get_1_mask (manifest[i].tag); |
|
249 if (fallback_plan->mask_array[j]) |
|
250 { |
|
251 fallback_plan->lookup_array[j] = const_cast<OT::SubstLookup*> (&(&manifest+manifest[i].lookupOffset)); |
|
252 if (fallback_plan->lookup_array[j]) |
|
253 { |
|
254 fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); |
|
255 j++; |
|
256 } |
|
257 } |
|
258 } |
|
259 |
|
260 fallback_plan->num_lookups = j; |
|
261 fallback_plan->free_lookups = false; |
|
262 |
|
263 return j > 0; |
|
264 #else |
|
265 return false; |
|
266 #endif |
|
267 } |
|
268 |
|
269 static bool |
|
270 arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan, |
|
271 const hb_ot_shape_plan_t *plan, |
|
272 hb_font_t *font) |
|
273 { |
|
274 ASSERT_STATIC (ARRAY_LENGTH_CONST(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS); |
|
275 unsigned int j = 0; |
|
276 for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++) |
|
277 { |
|
278 fallback_plan->mask_array[j] = plan->map.get_1_mask (arabic_fallback_features[i]); |
|
279 if (fallback_plan->mask_array[j]) |
|
280 { |
|
281 fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i); |
|
282 if (fallback_plan->lookup_array[j]) |
|
283 { |
|
284 fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); |
|
285 j++; |
|
286 } |
|
287 } |
|
288 } |
|
289 |
|
290 fallback_plan->num_lookups = j; |
|
291 fallback_plan->free_lookups = true; |
|
292 |
|
293 return j > 0; |
|
294 } |
|
295 |
|
296 static arabic_fallback_plan_t * |
|
297 arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, |
|
298 hb_font_t *font) |
|
299 { |
|
300 arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t)); |
|
301 if (unlikely (!fallback_plan)) |
|
302 return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil); |
|
303 |
|
304 fallback_plan->num_lookups = 0; |
|
305 fallback_plan->free_lookups = false; |
|
306 |
|
307 /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms, |
|
308 * in case the font has cmap entries for the presentation-forms characters. */ |
|
309 if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font)) |
|
310 return fallback_plan; |
|
311 |
|
312 /* See if this looks like a Windows-1256-encoded font. If it does, use a |
|
313 * hand-coded GSUB table. */ |
|
314 if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font)) |
|
315 return fallback_plan; |
|
316 |
|
317 free (fallback_plan); |
|
318 return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil); |
|
319 } |
|
320 |
|
321 static void |
|
322 arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) |
|
323 { |
|
324 if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil) |
|
325 return; |
|
326 |
|
327 for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) |
|
328 if (fallback_plan->lookup_array[i]) |
|
329 { |
|
330 fallback_plan->accel_array[i].fini (); |
|
331 if (fallback_plan->free_lookups) |
|
332 free (fallback_plan->lookup_array[i]); |
|
333 } |
|
334 |
|
335 free (fallback_plan); |
|
336 } |
|
337 |
|
338 static void |
|
339 arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan, |
|
340 hb_font_t *font, |
|
341 hb_buffer_t *buffer) |
|
342 { |
|
343 OT::hb_apply_context_t c (0, font, buffer); |
|
344 for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) |
|
345 if (fallback_plan->lookup_array[i]) { |
|
346 c.set_lookup_mask (fallback_plan->mask_array[i]); |
|
347 hb_ot_layout_substitute_lookup (&c, |
|
348 *fallback_plan->lookup_array[i], |
|
349 fallback_plan->accel_array[i]); |
|
350 } |
|
351 } |
|
352 |
|
353 |
|
354 #endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */ |