• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 1998-2004  David Turner and Werner Lemberg
3  * Copyright © 2006  Behdad Esfahbod
4  * Copyright © 2007,2008,2009  Red Hat, Inc.
5  * Copyright © 2012,2013  Google, Inc.
6  *
7  *  This is part of HarfBuzz, a text shaping library.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and its documentation for any purpose, provided that the
12  * above copyright notice and the following two paragraphs appear in
13  * all copies of this software.
14  *
15  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
16  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
17  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
18  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
19  * DAMAGE.
20  *
21  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
22  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
23  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
24  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
25  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
26  *
27  * Red Hat Author(s): Behdad Esfahbod
28  * Google Author(s): Behdad Esfahbod
29  */
30 
31 #include "hb-ot-layout-private.hh"
32 
33 #include "hb-ot-layout-gdef-table.hh"
34 #include "hb-ot-layout-gsub-table.hh"
35 #include "hb-ot-layout-gpos-table.hh"
36 #include "hb-ot-layout-jstf-table.hh"
37 
38 #include "hb-ot-map-private.hh"
39 
40 #include <stdlib.h>
41 #include <string.h>
42 
43 
HB_SHAPER_DATA_ENSURE_DECLARE(ot,face)44 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
45 
46 hb_ot_layout_t *
47 _hb_ot_layout_create (hb_face_t *face)
48 {
49   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
50   if (unlikely (!layout))
51     return NULL;
52 
53   layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
54   layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
55 
56   layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
57   layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
58 
59   layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
60   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
61 
62   layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
63   layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
64 
65   layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
66   layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
67 
68   if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) ||
69 		(layout->gpos_lookup_count && !layout->gpos_accels)))
70   {
71     _hb_ot_layout_destroy (layout);
72     return NULL;
73   }
74 
75   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
76     layout->gsub_accels[i].init (layout->gsub->get_lookup (i));
77   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
78     layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
79 
80   return layout;
81 }
82 
83 void
_hb_ot_layout_destroy(hb_ot_layout_t * layout)84 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
85 {
86   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
87     layout->gsub_accels[i].fini (layout->gsub->get_lookup (i));
88   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
89     layout->gpos_accels[i].fini (layout->gpos->get_lookup (i));
90 
91   free (layout->gsub_accels);
92   free (layout->gpos_accels);
93 
94   hb_blob_destroy (layout->gdef_blob);
95   hb_blob_destroy (layout->gsub_blob);
96   hb_blob_destroy (layout->gpos_blob);
97 
98   free (layout);
99 }
100 
101 static inline const OT::GDEF&
_get_gdef(hb_face_t * face)102 _get_gdef (hb_face_t *face)
103 {
104   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
105   return *hb_ot_layout_from_face (face)->gdef;
106 }
107 static inline const OT::GSUB&
_get_gsub(hb_face_t * face)108 _get_gsub (hb_face_t *face)
109 {
110   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
111   return *hb_ot_layout_from_face (face)->gsub;
112 }
113 static inline const OT::GPOS&
_get_gpos(hb_face_t * face)114 _get_gpos (hb_face_t *face)
115 {
116   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
117   return *hb_ot_layout_from_face (face)->gpos;
118 }
119 
120 
121 /*
122  * GDEF
123  */
124 
125 hb_bool_t
hb_ot_layout_has_glyph_classes(hb_face_t * face)126 hb_ot_layout_has_glyph_classes (hb_face_t *face)
127 {
128   return _get_gdef (face).has_glyph_classes ();
129 }
130 
131 hb_ot_layout_glyph_class_t
hb_ot_layout_get_glyph_class(hb_face_t * face,hb_codepoint_t glyph)132 hb_ot_layout_get_glyph_class (hb_face_t      *face,
133 			      hb_codepoint_t  glyph)
134 {
135   return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
136 }
137 
138 void
hb_ot_layout_get_glyphs_in_class(hb_face_t * face,hb_ot_layout_glyph_class_t klass,hb_set_t * glyphs)139 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
140 				  hb_ot_layout_glyph_class_t  klass,
141 				  hb_set_t                   *glyphs /* OUT */)
142 {
143   return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
144 }
145 
146 unsigned int
hb_ot_layout_get_attach_points(hb_face_t * face,hb_codepoint_t glyph,unsigned int start_offset,unsigned int * point_count,unsigned int * point_array)147 hb_ot_layout_get_attach_points (hb_face_t      *face,
148 				hb_codepoint_t  glyph,
149 				unsigned int    start_offset,
150 				unsigned int   *point_count /* IN/OUT */,
151 				unsigned int   *point_array /* OUT */)
152 {
153   return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
154 }
155 
156 unsigned int
hb_ot_layout_get_ligature_carets(hb_font_t * font,hb_direction_t direction,hb_codepoint_t glyph,unsigned int start_offset,unsigned int * caret_count,int * caret_array)157 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
158 				  hb_direction_t  direction,
159 				  hb_codepoint_t  glyph,
160 				  unsigned int    start_offset,
161 				  unsigned int   *caret_count /* IN/OUT */,
162 				  int            *caret_array /* OUT */)
163 {
164   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
165 }
166 
167 
168 /*
169  * GSUB/GPOS
170  */
171 
172 static const OT::GSUBGPOS&
get_gsubgpos_table(hb_face_t * face,hb_tag_t table_tag)173 get_gsubgpos_table (hb_face_t *face,
174 		    hb_tag_t   table_tag)
175 {
176   switch (table_tag) {
177     case HB_OT_TAG_GSUB: return _get_gsub (face);
178     case HB_OT_TAG_GPOS: return _get_gpos (face);
179     default:             return OT::Null(OT::GSUBGPOS);
180   }
181 }
182 
183 
184 unsigned int
hb_ot_layout_table_get_script_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int start_offset,unsigned int * script_count,hb_tag_t * script_tags)185 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
186 				    hb_tag_t      table_tag,
187 				    unsigned int  start_offset,
188 				    unsigned int *script_count /* IN/OUT */,
189 				    hb_tag_t     *script_tags /* OUT */)
190 {
191   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
192 
193   return g.get_script_tags (start_offset, script_count, script_tags);
194 }
195 
196 #define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
197 
198 hb_bool_t
hb_ot_layout_table_find_script(hb_face_t * face,hb_tag_t table_tag,hb_tag_t script_tag,unsigned int * script_index)199 hb_ot_layout_table_find_script (hb_face_t    *face,
200 				hb_tag_t      table_tag,
201 				hb_tag_t      script_tag,
202 				unsigned int *script_index)
203 {
204   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
205   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
206 
207   if (g.find_script_index (script_tag, script_index))
208     return true;
209 
210   /* try finding 'DFLT' */
211   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
212     return false;
213 
214   /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
215    * including many versions of DejaVu Sans Mono! */
216   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
217     return false;
218 
219   /* try with 'latn'; some old fonts put their features there even though
220      they're really trying to support Thai, for example :( */
221   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
222     return false;
223 
224   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
225   return false;
226 }
227 
228 hb_bool_t
hb_ot_layout_table_choose_script(hb_face_t * face,hb_tag_t table_tag,const hb_tag_t * script_tags,unsigned int * script_index,hb_tag_t * chosen_script)229 hb_ot_layout_table_choose_script (hb_face_t      *face,
230 				  hb_tag_t        table_tag,
231 				  const hb_tag_t *script_tags,
232 				  unsigned int   *script_index,
233 				  hb_tag_t       *chosen_script)
234 {
235   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
236   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
237 
238   while (*script_tags)
239   {
240     if (g.find_script_index (*script_tags, script_index)) {
241       if (chosen_script)
242         *chosen_script = *script_tags;
243       return true;
244     }
245     script_tags++;
246   }
247 
248   /* try finding 'DFLT' */
249   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
250     if (chosen_script)
251       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
252     return false;
253   }
254 
255   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
256   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
257     if (chosen_script)
258       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
259     return false;
260   }
261 
262   /* try with 'latn'; some old fonts put their features there even though
263      they're really trying to support Thai, for example :( */
264   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
265     if (chosen_script)
266       *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
267     return false;
268   }
269 
270   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
271   if (chosen_script)
272     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
273   return false;
274 }
275 
276 unsigned int
hb_ot_layout_table_get_feature_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int start_offset,unsigned int * feature_count,hb_tag_t * feature_tags)277 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
278 				     hb_tag_t      table_tag,
279 				     unsigned int  start_offset,
280 				     unsigned int *feature_count /* IN/OUT */,
281 				     hb_tag_t     *feature_tags /* OUT */)
282 {
283   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
284 
285   return g.get_feature_tags (start_offset, feature_count, feature_tags);
286 }
287 
288 
289 unsigned int
hb_ot_layout_script_get_language_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int start_offset,unsigned int * language_count,hb_tag_t * language_tags)290 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
291 				       hb_tag_t      table_tag,
292 				       unsigned int  script_index,
293 				       unsigned int  start_offset,
294 				       unsigned int *language_count /* IN/OUT */,
295 				       hb_tag_t     *language_tags /* OUT */)
296 {
297   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
298 
299   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
300 }
301 
302 hb_bool_t
hb_ot_layout_script_find_language(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,hb_tag_t language_tag,unsigned int * language_index)303 hb_ot_layout_script_find_language (hb_face_t    *face,
304 				   hb_tag_t      table_tag,
305 				   unsigned int  script_index,
306 				   hb_tag_t      language_tag,
307 				   unsigned int *language_index)
308 {
309   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
310   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
311 
312   if (s.find_lang_sys_index (language_tag, language_index))
313     return true;
314 
315   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
316   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
317     return false;
318 
319   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
320   return false;
321 }
322 
323 hb_bool_t
hb_ot_layout_language_get_required_feature_index(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,unsigned int * feature_index)324 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
325 						  hb_tag_t      table_tag,
326 						  unsigned int  script_index,
327 						  unsigned int  language_index,
328 						  unsigned int *feature_index)
329 {
330   const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
331 
332   if (feature_index) *feature_index = l.get_required_feature_index ();
333 
334   return l.has_required_feature ();
335 }
336 
337 unsigned int
hb_ot_layout_language_get_feature_indexes(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,unsigned int start_offset,unsigned int * feature_count,unsigned int * feature_indexes)338 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
339 					   hb_tag_t      table_tag,
340 					   unsigned int  script_index,
341 					   unsigned int  language_index,
342 					   unsigned int  start_offset,
343 					   unsigned int *feature_count /* IN/OUT */,
344 					   unsigned int *feature_indexes /* OUT */)
345 {
346   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
347   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
348 
349   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
350 }
351 
352 unsigned int
hb_ot_layout_language_get_feature_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,unsigned int start_offset,unsigned int * feature_count,hb_tag_t * feature_tags)353 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
354 					hb_tag_t      table_tag,
355 					unsigned int  script_index,
356 					unsigned int  language_index,
357 					unsigned int  start_offset,
358 					unsigned int *feature_count /* IN/OUT */,
359 					hb_tag_t     *feature_tags /* OUT */)
360 {
361   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
362   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
363 
364   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
365   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
366 
367   if (feature_tags) {
368     unsigned int count = *feature_count;
369     for (unsigned int i = 0; i < count; i++)
370       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
371   }
372 
373   return ret;
374 }
375 
376 
377 hb_bool_t
hb_ot_layout_language_find_feature(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,hb_tag_t feature_tag,unsigned int * feature_index)378 hb_ot_layout_language_find_feature (hb_face_t    *face,
379 				    hb_tag_t      table_tag,
380 				    unsigned int  script_index,
381 				    unsigned int  language_index,
382 				    hb_tag_t      feature_tag,
383 				    unsigned int *feature_index)
384 {
385   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
386   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
387   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
388 
389   unsigned int num_features = l.get_feature_count ();
390   for (unsigned int i = 0; i < num_features; i++) {
391     unsigned int f_index = l.get_feature_index (i);
392 
393     if (feature_tag == g.get_feature_tag (f_index)) {
394       if (feature_index) *feature_index = f_index;
395       return true;
396     }
397   }
398 
399   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
400   return false;
401 }
402 
403 unsigned int
hb_ot_layout_feature_get_lookups(hb_face_t * face,hb_tag_t table_tag,unsigned int feature_index,unsigned int start_offset,unsigned int * lookup_count,unsigned int * lookup_indexes)404 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
405 				  hb_tag_t      table_tag,
406 				  unsigned int  feature_index,
407 				  unsigned int  start_offset,
408 				  unsigned int *lookup_count /* IN/OUT */,
409 				  unsigned int *lookup_indexes /* OUT */)
410 {
411   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
412   const OT::Feature &f = g.get_feature (feature_index);
413 
414   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
415 }
416 
417 unsigned int
hb_ot_layout_table_get_lookup_count(hb_face_t * face,hb_tag_t table_tag)418 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
419 				     hb_tag_t      table_tag)
420 {
421   switch (table_tag)
422   {
423     case HB_OT_TAG_GSUB:
424     {
425       return hb_ot_layout_from_face (face)->gsub_lookup_count;
426     }
427     case HB_OT_TAG_GPOS:
428     {
429       return hb_ot_layout_from_face (face)->gpos_lookup_count;
430     }
431   }
432   return 0;
433 }
434 
435 static void
_hb_ot_layout_collect_lookups_lookups(hb_face_t * face,hb_tag_t table_tag,unsigned int feature_index,hb_set_t * lookup_indexes)436 _hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
437 				       hb_tag_t        table_tag,
438 				       unsigned int    feature_index,
439 				       hb_set_t       *lookup_indexes /* OUT */)
440 {
441   unsigned int lookup_indices[32];
442   unsigned int offset, len;
443 
444   offset = 0;
445   do {
446     len = ARRAY_LENGTH (lookup_indices);
447     hb_ot_layout_feature_get_lookups (face,
448 				      table_tag,
449 				      feature_index,
450 				      offset, &len,
451 				      lookup_indices);
452 
453     for (unsigned int i = 0; i < len; i++)
454       lookup_indexes->add (lookup_indices[i]);
455 
456     offset += len;
457   } while (len == ARRAY_LENGTH (lookup_indices));
458 }
459 
460 static void
_hb_ot_layout_collect_lookups_features(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,const hb_tag_t * features,hb_set_t * lookup_indexes)461 _hb_ot_layout_collect_lookups_features (hb_face_t      *face,
462 					hb_tag_t        table_tag,
463 					unsigned int    script_index,
464 					unsigned int    language_index,
465 					const hb_tag_t *features,
466 					hb_set_t       *lookup_indexes /* OUT */)
467 {
468   if (!features)
469   {
470     unsigned int required_feature_index;
471     if (hb_ot_layout_language_get_required_feature_index (face,
472 							  table_tag,
473 							  script_index,
474 							  language_index,
475 							  &required_feature_index))
476       _hb_ot_layout_collect_lookups_lookups (face,
477 					     table_tag,
478 					     required_feature_index,
479 					     lookup_indexes);
480 
481     /* All features */
482     unsigned int feature_indices[32];
483     unsigned int offset, len;
484 
485     offset = 0;
486     do {
487       len = ARRAY_LENGTH (feature_indices);
488       hb_ot_layout_language_get_feature_indexes (face,
489 						 table_tag,
490 						 script_index,
491 						 language_index,
492 						 offset, &len,
493 						 feature_indices);
494 
495       for (unsigned int i = 0; i < len; i++)
496 	_hb_ot_layout_collect_lookups_lookups (face,
497 					       table_tag,
498 					       feature_indices[i],
499 					       lookup_indexes);
500 
501       offset += len;
502     } while (len == ARRAY_LENGTH (feature_indices));
503   }
504   else
505   {
506     for (; *features; features++)
507     {
508       unsigned int feature_index;
509       if (hb_ot_layout_language_find_feature (face,
510 					      table_tag,
511 					      script_index,
512 					      language_index,
513 					      *features,
514 					      &feature_index))
515         _hb_ot_layout_collect_lookups_lookups (face,
516 					       table_tag,
517 					       feature_index,
518 					       lookup_indexes);
519     }
520   }
521 }
522 
523 static void
_hb_ot_layout_collect_lookups_languages(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,const hb_tag_t * languages,const hb_tag_t * features,hb_set_t * lookup_indexes)524 _hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
525 					 hb_tag_t        table_tag,
526 					 unsigned int    script_index,
527 					 const hb_tag_t *languages,
528 					 const hb_tag_t *features,
529 					 hb_set_t       *lookup_indexes /* OUT */)
530 {
531   _hb_ot_layout_collect_lookups_features (face,
532 					  table_tag,
533 					  script_index,
534 					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
535 					  features,
536 					  lookup_indexes);
537 
538   if (!languages)
539   {
540     /* All languages */
541     unsigned int count = hb_ot_layout_script_get_language_tags (face,
542 								table_tag,
543 								script_index,
544 								0, NULL, NULL);
545     for (unsigned int language_index = 0; language_index < count; language_index++)
546       _hb_ot_layout_collect_lookups_features (face,
547 					      table_tag,
548 					      script_index,
549 					      language_index,
550 					      features,
551 					      lookup_indexes);
552   }
553   else
554   {
555     for (; *languages; languages++)
556     {
557       unsigned int language_index;
558       if (hb_ot_layout_script_find_language (face,
559 					     table_tag,
560 					     script_index,
561 					     *languages,
562 					     &language_index))
563         _hb_ot_layout_collect_lookups_features (face,
564 						table_tag,
565 						script_index,
566 						language_index,
567 						features,
568 						lookup_indexes);
569     }
570   }
571 }
572 
573 void
hb_ot_layout_collect_lookups(hb_face_t * face,hb_tag_t table_tag,const hb_tag_t * scripts,const hb_tag_t * languages,const hb_tag_t * features,hb_set_t * lookup_indexes)574 hb_ot_layout_collect_lookups (hb_face_t      *face,
575 			      hb_tag_t        table_tag,
576 			      const hb_tag_t *scripts,
577 			      const hb_tag_t *languages,
578 			      const hb_tag_t *features,
579 			      hb_set_t       *lookup_indexes /* OUT */)
580 {
581   if (!scripts)
582   {
583     /* All scripts */
584     unsigned int count = hb_ot_layout_table_get_script_tags (face,
585 							     table_tag,
586 							     0, NULL, NULL);
587     for (unsigned int script_index = 0; script_index < count; script_index++)
588       _hb_ot_layout_collect_lookups_languages (face,
589 					       table_tag,
590 					       script_index,
591 					       languages,
592 					       features,
593 					       lookup_indexes);
594   }
595   else
596   {
597     for (; *scripts; scripts++)
598     {
599       unsigned int script_index;
600       if (hb_ot_layout_table_find_script (face,
601 					  table_tag,
602 					  *scripts,
603 					  &script_index))
604         _hb_ot_layout_collect_lookups_languages (face,
605 						 table_tag,
606 						 script_index,
607 						 languages,
608 						 features,
609 						 lookup_indexes);
610     }
611   }
612 }
613 
614 void
hb_ot_layout_lookup_collect_glyphs(hb_face_t * face,hb_tag_t table_tag,unsigned int lookup_index,hb_set_t * glyphs_before,hb_set_t * glyphs_input,hb_set_t * glyphs_after,hb_set_t * glyphs_output)615 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
616 				    hb_tag_t      table_tag,
617 				    unsigned int  lookup_index,
618 				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
619 				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
620 				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
621 				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
622 {
623   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
624 
625   OT::hb_collect_glyphs_context_t c (face,
626 				     glyphs_before,
627 				     glyphs_input,
628 				     glyphs_after,
629 				     glyphs_output);
630 
631   switch (table_tag)
632   {
633     case HB_OT_TAG_GSUB:
634     {
635       const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
636       l.collect_glyphs (&c);
637       return;
638     }
639     case HB_OT_TAG_GPOS:
640     {
641       const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
642       l.collect_glyphs (&c);
643       return;
644     }
645   }
646 }
647 
648 
649 /*
650  * OT::GSUB
651  */
652 
653 hb_bool_t
hb_ot_layout_has_substitution(hb_face_t * face)654 hb_ot_layout_has_substitution (hb_face_t *face)
655 {
656   return &_get_gsub (face) != &OT::Null(OT::GSUB);
657 }
658 
659 hb_bool_t
hb_ot_layout_lookup_would_substitute(hb_face_t * face,unsigned int lookup_index,const hb_codepoint_t * glyphs,unsigned int glyphs_length,hb_bool_t zero_context)660 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
661 				      unsigned int          lookup_index,
662 				      const hb_codepoint_t *glyphs,
663 				      unsigned int          glyphs_length,
664 				      hb_bool_t             zero_context)
665 {
666   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
667   return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
668 }
669 
670 hb_bool_t
hb_ot_layout_lookup_would_substitute_fast(hb_face_t * face,unsigned int lookup_index,const hb_codepoint_t * glyphs,unsigned int glyphs_length,hb_bool_t zero_context)671 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
672 					   unsigned int          lookup_index,
673 					   const hb_codepoint_t *glyphs,
674 					   unsigned int          glyphs_length,
675 					   hb_bool_t             zero_context)
676 {
677   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
678   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
679 
680   const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
681 
682   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest);
683 }
684 
685 void
hb_ot_layout_substitute_start(hb_font_t * font,hb_buffer_t * buffer)686 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
687 {
688   OT::GSUB::substitute_start (font, buffer);
689 }
690 
691 void
hb_ot_layout_substitute_finish(hb_font_t * font,hb_buffer_t * buffer)692 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
693 {
694   OT::GSUB::substitute_finish (font, buffer);
695 }
696 
697 void
hb_ot_layout_lookup_substitute_closure(hb_face_t * face,unsigned int lookup_index,hb_set_t * glyphs)698 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
699 				        unsigned int  lookup_index,
700 				        hb_set_t     *glyphs)
701 {
702   OT::hb_closure_context_t c (face, glyphs);
703 
704   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
705 
706   l.closure (&c);
707 }
708 
709 /*
710  * OT::GPOS
711  */
712 
713 hb_bool_t
hb_ot_layout_has_positioning(hb_face_t * face)714 hb_ot_layout_has_positioning (hb_face_t *face)
715 {
716   return &_get_gpos (face) != &OT::Null(OT::GPOS);
717 }
718 
719 void
hb_ot_layout_position_start(hb_font_t * font,hb_buffer_t * buffer)720 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
721 {
722   OT::GPOS::position_start (font, buffer);
723 }
724 
725 void
hb_ot_layout_position_finish(hb_font_t * font,hb_buffer_t * buffer)726 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
727 {
728   OT::GPOS::position_finish (font, buffer);
729 }
730 
731 hb_bool_t
hb_ot_layout_get_size_params(hb_face_t * face,unsigned int * design_size,unsigned int * subfamily_id,unsigned int * subfamily_name_id,unsigned int * range_start,unsigned int * range_end)732 hb_ot_layout_get_size_params (hb_face_t    *face,
733 			      unsigned int *design_size,       /* OUT.  May be NULL */
734 			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
735 			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
736 			      unsigned int *range_start,       /* OUT.  May be NULL */
737 			      unsigned int *range_end          /* OUT.  May be NULL */)
738 {
739   const OT::GPOS &gpos = _get_gpos (face);
740   const hb_tag_t tag = HB_TAG ('s','i','z','e');
741 
742   unsigned int num_features = gpos.get_feature_count ();
743   for (unsigned int i = 0; i < num_features; i++)
744   {
745     if (tag == gpos.get_feature_tag (i))
746     {
747       const OT::Feature &f = gpos.get_feature (i);
748       const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
749 
750       if (params.designSize)
751       {
752 #define PARAM(a, A) if (a) *a = params.A
753 	PARAM (design_size, designSize);
754 	PARAM (subfamily_id, subfamilyID);
755 	PARAM (subfamily_name_id, subfamilyNameID);
756 	PARAM (range_start, rangeStart);
757 	PARAM (range_end, rangeEnd);
758 #undef PARAM
759 
760 	return true;
761       }
762     }
763   }
764 
765 #define PARAM(a, A) if (a) *a = 0
766   PARAM (design_size, designSize);
767   PARAM (subfamily_id, subfamilyID);
768   PARAM (subfamily_name_id, subfamilyNameID);
769   PARAM (range_start, rangeStart);
770   PARAM (range_end, rangeEnd);
771 #undef PARAM
772 
773   return false;
774 }
775 
776 
777 /*
778  * Parts of different types are implemented here such that they have direct
779  * access to GSUB/GPOS lookups.
780  */
781 
782 
783 struct GSUBProxy
784 {
785   static const unsigned int table_index = 0;
786   static const bool inplace = false;
787   typedef OT::SubstLookup Lookup;
788 
GSUBProxyGSUBProxy789   GSUBProxy (hb_face_t *face) :
790     table (*hb_ot_layout_from_face (face)->gsub),
791     accels (hb_ot_layout_from_face (face)->gsub_accels) {}
792 
793   const OT::GSUB &table;
794   const hb_ot_layout_lookup_accelerator_t *accels;
795 };
796 
797 struct GPOSProxy
798 {
799   static const unsigned int table_index = 1;
800   static const bool inplace = true;
801   typedef OT::PosLookup Lookup;
802 
GPOSProxyGPOSProxy803   GPOSProxy (hb_face_t *face) :
804     table (*hb_ot_layout_from_face (face)->gpos),
805     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
806 
807   const OT::GPOS &table;
808   const hb_ot_layout_lookup_accelerator_t *accels;
809 };
810 
811 
812 template <typename Lookup>
apply_once(OT::hb_apply_context_t * c,const Lookup & lookup)813 static inline bool apply_once (OT::hb_apply_context_t *c,
814 			       const Lookup &lookup)
815 {
816   if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
817     return false;
818   return lookup.dispatch (c);
819 }
820 
821 template <typename Proxy>
822 static inline bool
apply_string(OT::hb_apply_context_t * c,const typename Proxy::Lookup & lookup,const hb_ot_layout_lookup_accelerator_t & accel)823 apply_string (OT::hb_apply_context_t *c,
824 	      const typename Proxy::Lookup &lookup,
825 	      const hb_ot_layout_lookup_accelerator_t &accel)
826 {
827   bool ret = false;
828   hb_buffer_t *buffer = c->buffer;
829 
830   if (unlikely (!buffer->len || !c->lookup_mask))
831     return false;
832 
833   c->set_lookup (lookup);
834 
835   if (likely (!lookup.is_reverse ()))
836   {
837     /* in/out forward substitution/positioning */
838     if (Proxy::table_index == 0)
839       buffer->clear_output ();
840     buffer->idx = 0;
841 
842     while (buffer->idx < buffer->len)
843     {
844       if (accel.digest.may_have (buffer->cur().codepoint) &&
845 	  (buffer->cur().mask & c->lookup_mask) &&
846 	  apply_once (c, lookup))
847 	ret = true;
848       else
849 	buffer->next_glyph ();
850     }
851     if (ret)
852     {
853       if (!Proxy::inplace)
854 	buffer->swap_buffers ();
855       else
856         assert (!buffer->has_separate_output ());
857     }
858   }
859   else
860   {
861     /* in-place backward substitution/positioning */
862     if (Proxy::table_index == 0)
863       buffer->remove_output ();
864     buffer->idx = buffer->len - 1;
865     do
866     {
867       if (accel.digest.may_have (buffer->cur().codepoint) &&
868 	  (buffer->cur().mask & c->lookup_mask) &&
869 	  apply_once (c, lookup))
870 	ret = true;
871       /* The reverse lookup doesn't "advance" cursor (for good reason). */
872       buffer->idx--;
873 
874     }
875     while ((int) buffer->idx >= 0);
876   }
877 
878   return ret;
879 }
880 
881 template <typename Proxy>
apply(const Proxy & proxy,const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const882 inline void hb_ot_map_t::apply (const Proxy &proxy,
883 				const hb_ot_shape_plan_t *plan,
884 				hb_font_t *font,
885 				hb_buffer_t *buffer) const
886 {
887   const unsigned int table_index = proxy.table_index;
888   unsigned int i = 0;
889   OT::hb_apply_context_t c (table_index, font, buffer);
890   c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
891 
892   for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
893     const stage_map_t *stage = &stages[table_index][stage_index];
894     for (; i < stage->last_lookup; i++)
895     {
896       unsigned int lookup_index = lookups[table_index][i].index;
897       c.set_lookup_mask (lookups[table_index][i].mask);
898       c.set_auto_zwj (lookups[table_index][i].auto_zwj);
899       apply_string<Proxy> (&c,
900 			   proxy.table.get_lookup (lookup_index),
901 			   proxy.accels[lookup_index]);
902     }
903 
904     if (stage->pause_func)
905     {
906       buffer->clear_output ();
907       stage->pause_func (plan, font, buffer);
908     }
909   }
910 }
911 
substitute(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const912 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
913 {
914   GSUBProxy proxy (font->face);
915   apply (proxy, plan, font, buffer);
916 }
917 
position(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const918 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
919 {
920   GPOSProxy proxy (font->face);
921   apply (proxy, plan, font, buffer);
922 }
923 
924 HB_INTERNAL void
hb_ot_layout_substitute_lookup(OT::hb_apply_context_t * c,const OT::SubstLookup & lookup,const hb_ot_layout_lookup_accelerator_t & accel)925 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
926 				const OT::SubstLookup &lookup,
927 				const hb_ot_layout_lookup_accelerator_t &accel)
928 {
929   apply_string<GSUBProxy> (c, lookup, accel);
930 }
931