• 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  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 
37 #include <stdlib.h>
38 #include <string.h>
39 
40 
HB_SHAPER_DATA_ENSURE_DECLARE(ot,face)41 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
42 
43 hb_ot_layout_t *
44 _hb_ot_layout_create (hb_face_t *face)
45 {
46   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
47   if (unlikely (!layout))
48     return NULL;
49 
50   layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
51   layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
52 
53   layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
54   layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
55 
56   layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
57   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
58 
59   layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
60   layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
61 
62   layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t));
63   layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t));
64 
65   if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) ||
66 		(layout->gpos_lookup_count && !layout->gpos_digests)))
67   {
68     _hb_ot_layout_destroy (layout);
69     return NULL;
70   }
71 
72   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
73     layout->gsub->get_lookup (i).add_coverage (&layout->gsub_digests[i]);
74   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
75     layout->gpos->get_lookup (i).add_coverage (&layout->gpos_digests[i]);
76 
77   return layout;
78 }
79 
80 void
_hb_ot_layout_destroy(hb_ot_layout_t * layout)81 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
82 {
83   hb_blob_destroy (layout->gdef_blob);
84   hb_blob_destroy (layout->gsub_blob);
85   hb_blob_destroy (layout->gpos_blob);
86 
87   free (layout->gsub_digests);
88   free (layout->gpos_digests);
89 
90   free (layout);
91 }
92 
93 static inline const OT::GDEF&
_get_gdef(hb_face_t * face)94 _get_gdef (hb_face_t *face)
95 {
96   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
97   return *hb_ot_layout_from_face (face)->gdef;
98 }
99 static inline const OT::GSUB&
_get_gsub(hb_face_t * face)100 _get_gsub (hb_face_t *face)
101 {
102   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
103   return *hb_ot_layout_from_face (face)->gsub;
104 }
105 static inline const OT::GPOS&
_get_gpos(hb_face_t * face)106 _get_gpos (hb_face_t *face)
107 {
108   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
109   return *hb_ot_layout_from_face (face)->gpos;
110 }
111 
112 
113 /*
114  * GDEF
115  */
116 
117 hb_bool_t
hb_ot_layout_has_glyph_classes(hb_face_t * face)118 hb_ot_layout_has_glyph_classes (hb_face_t *face)
119 {
120   return _get_gdef (face).has_glyph_classes ();
121 }
122 
123 hb_ot_layout_glyph_class_t
hb_ot_layout_get_glyph_class(hb_face_t * face,hb_codepoint_t glyph)124 hb_ot_layout_get_glyph_class (hb_face_t      *face,
125 			      hb_codepoint_t  glyph)
126 {
127   return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
128 }
129 
130 void
hb_ot_layout_get_glyphs_in_class(hb_face_t * face,hb_ot_layout_glyph_class_t klass,hb_set_t * glyphs)131 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
132 				  hb_ot_layout_glyph_class_t  klass,
133 				  hb_set_t                   *glyphs /* OUT */)
134 {
135   return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
136 }
137 
138 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)139 hb_ot_layout_get_attach_points (hb_face_t      *face,
140 				hb_codepoint_t  glyph,
141 				unsigned int    start_offset,
142 				unsigned int   *point_count /* IN/OUT */,
143 				unsigned int   *point_array /* OUT */)
144 {
145   return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
146 }
147 
148 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)149 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
150 				  hb_direction_t  direction,
151 				  hb_codepoint_t  glyph,
152 				  unsigned int    start_offset,
153 				  unsigned int   *caret_count /* IN/OUT */,
154 				  int            *caret_array /* OUT */)
155 {
156   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
157 }
158 
159 
160 /*
161  * GSUB/GPOS
162  */
163 
164 static const OT::GSUBGPOS&
get_gsubgpos_table(hb_face_t * face,hb_tag_t table_tag)165 get_gsubgpos_table (hb_face_t *face,
166 		    hb_tag_t   table_tag)
167 {
168   switch (table_tag) {
169     case HB_OT_TAG_GSUB: return _get_gsub (face);
170     case HB_OT_TAG_GPOS: return _get_gpos (face);
171     default:             return OT::Null(OT::GSUBGPOS);
172   }
173 }
174 
175 
176 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)177 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
178 				    hb_tag_t      table_tag,
179 				    unsigned int  start_offset,
180 				    unsigned int *script_count /* IN/OUT */,
181 				    hb_tag_t     *script_tags /* OUT */)
182 {
183   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
184 
185   return g.get_script_tags (start_offset, script_count, script_tags);
186 }
187 
188 #define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
189 
190 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)191 hb_ot_layout_table_find_script (hb_face_t    *face,
192 				hb_tag_t      table_tag,
193 				hb_tag_t      script_tag,
194 				unsigned int *script_index)
195 {
196   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
197   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
198 
199   if (g.find_script_index (script_tag, script_index))
200     return true;
201 
202   /* try finding 'DFLT' */
203   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
204     return false;
205 
206   /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
207    * including many versions of DejaVu Sans Mono! */
208   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
209     return false;
210 
211   /* try with 'latn'; some old fonts put their features there even though
212      they're really trying to support Thai, for example :( */
213   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
214     return false;
215 
216   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
217   return false;
218 }
219 
220 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)221 hb_ot_layout_table_choose_script (hb_face_t      *face,
222 				  hb_tag_t        table_tag,
223 				  const hb_tag_t *script_tags,
224 				  unsigned int   *script_index,
225 				  hb_tag_t       *chosen_script)
226 {
227   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
228   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
229 
230   while (*script_tags)
231   {
232     if (g.find_script_index (*script_tags, script_index)) {
233       if (chosen_script)
234         *chosen_script = *script_tags;
235       return true;
236     }
237     script_tags++;
238   }
239 
240   /* try finding 'DFLT' */
241   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
242     if (chosen_script)
243       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
244     return false;
245   }
246 
247   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
248   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
249     if (chosen_script)
250       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
251     return false;
252   }
253 
254   /* try with 'latn'; some old fonts put their features there even though
255      they're really trying to support Thai, for example :( */
256   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
257     if (chosen_script)
258       *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
259     return false;
260   }
261 
262   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
263   if (chosen_script)
264     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
265   return false;
266 }
267 
268 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)269 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
270 				     hb_tag_t      table_tag,
271 				     unsigned int  start_offset,
272 				     unsigned int *feature_count /* IN/OUT */,
273 				     hb_tag_t     *feature_tags /* OUT */)
274 {
275   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
276 
277   return g.get_feature_tags (start_offset, feature_count, feature_tags);
278 }
279 
280 
281 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)282 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
283 				       hb_tag_t      table_tag,
284 				       unsigned int  script_index,
285 				       unsigned int  start_offset,
286 				       unsigned int *language_count /* IN/OUT */,
287 				       hb_tag_t     *language_tags /* OUT */)
288 {
289   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
290 
291   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
292 }
293 
294 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)295 hb_ot_layout_script_find_language (hb_face_t    *face,
296 				   hb_tag_t      table_tag,
297 				   unsigned int  script_index,
298 				   hb_tag_t      language_tag,
299 				   unsigned int *language_index)
300 {
301   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
302   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
303 
304   if (s.find_lang_sys_index (language_tag, language_index))
305     return true;
306 
307   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
308   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
309     return false;
310 
311   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
312   return false;
313 }
314 
315 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)316 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
317 						  hb_tag_t      table_tag,
318 						  unsigned int  script_index,
319 						  unsigned int  language_index,
320 						  unsigned int *feature_index)
321 {
322   const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
323 
324   if (feature_index) *feature_index = l.get_required_feature_index ();
325 
326   return l.has_required_feature ();
327 }
328 
329 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)330 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
331 					   hb_tag_t      table_tag,
332 					   unsigned int  script_index,
333 					   unsigned int  language_index,
334 					   unsigned int  start_offset,
335 					   unsigned int *feature_count /* IN/OUT */,
336 					   unsigned int *feature_indexes /* OUT */)
337 {
338   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
339   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
340 
341   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
342 }
343 
344 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)345 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
346 					hb_tag_t      table_tag,
347 					unsigned int  script_index,
348 					unsigned int  language_index,
349 					unsigned int  start_offset,
350 					unsigned int *feature_count /* IN/OUT */,
351 					hb_tag_t     *feature_tags /* OUT */)
352 {
353   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
354   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
355 
356   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
357   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
358 
359   if (feature_tags) {
360     unsigned int count = *feature_count;
361     for (unsigned int i = 0; i < count; i++)
362       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
363   }
364 
365   return ret;
366 }
367 
368 
369 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)370 hb_ot_layout_language_find_feature (hb_face_t    *face,
371 				    hb_tag_t      table_tag,
372 				    unsigned int  script_index,
373 				    unsigned int  language_index,
374 				    hb_tag_t      feature_tag,
375 				    unsigned int *feature_index)
376 {
377   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
378   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
379   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
380 
381   unsigned int num_features = l.get_feature_count ();
382   for (unsigned int i = 0; i < num_features; i++) {
383     unsigned int f_index = l.get_feature_index (i);
384 
385     if (feature_tag == g.get_feature_tag (f_index)) {
386       if (feature_index) *feature_index = f_index;
387       return true;
388     }
389   }
390 
391   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
392   return false;
393 }
394 
395 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)396 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
397 				  hb_tag_t      table_tag,
398 				  unsigned int  feature_index,
399 				  unsigned int  start_offset,
400 				  unsigned int *lookup_count /* IN/OUT */,
401 				  unsigned int *lookup_indexes /* OUT */)
402 {
403   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
404   const OT::Feature &f = g.get_feature (feature_index);
405 
406   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
407 }
408 
409 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)410 _hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
411 				       hb_tag_t        table_tag,
412 				       unsigned int    feature_index,
413 				       hb_set_t       *lookup_indexes /* OUT */)
414 {
415   unsigned int lookup_indices[32];
416   unsigned int offset, len;
417 
418   offset = 0;
419   do {
420     len = ARRAY_LENGTH (lookup_indices);
421     hb_ot_layout_feature_get_lookups (face,
422 				      table_tag,
423 				      feature_index,
424 				      offset, &len,
425 				      lookup_indices);
426 
427     for (unsigned int i = 0; i < len; i++)
428       lookup_indexes->add (lookup_indices[i]);
429 
430     offset += len;
431   } while (len == ARRAY_LENGTH (lookup_indices));
432 }
433 
434 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)435 _hb_ot_layout_collect_lookups_features (hb_face_t      *face,
436 					hb_tag_t        table_tag,
437 					unsigned int    script_index,
438 					unsigned int    language_index,
439 					const hb_tag_t *features,
440 					hb_set_t       *lookup_indexes /* OUT */)
441 {
442   unsigned int required_feature_index;
443   if (hb_ot_layout_language_get_required_feature_index (face,
444 							table_tag,
445 							script_index,
446 							language_index,
447 							&required_feature_index))
448     _hb_ot_layout_collect_lookups_lookups (face,
449 					   table_tag,
450 					   required_feature_index,
451 					   lookup_indexes);
452 
453   if (!features)
454   {
455     /* All features */
456     unsigned int feature_indices[32];
457     unsigned int offset, len;
458 
459     offset = 0;
460     do {
461       len = ARRAY_LENGTH (feature_indices);
462       hb_ot_layout_language_get_feature_indexes (face,
463 						 table_tag,
464 						 script_index,
465 						 language_index,
466 						 offset, &len,
467 						 feature_indices);
468 
469       for (unsigned int i = 0; i < len; i++)
470 	_hb_ot_layout_collect_lookups_lookups (face,
471 					       table_tag,
472 					       feature_indices[i],
473 					       lookup_indexes);
474 
475       offset += len;
476     } while (len == ARRAY_LENGTH (feature_indices));
477   }
478   else
479   {
480     for (; *features; features++)
481     {
482       unsigned int feature_index;
483       if (hb_ot_layout_language_find_feature (face,
484 					      table_tag,
485 					      script_index,
486 					      language_index,
487 					      *features,
488 					      &feature_index))
489         _hb_ot_layout_collect_lookups_lookups (face,
490 					       table_tag,
491 					       feature_index,
492 					       lookup_indexes);
493     }
494   }
495 }
496 
497 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)498 _hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
499 					 hb_tag_t        table_tag,
500 					 unsigned int    script_index,
501 					 const hb_tag_t *languages,
502 					 const hb_tag_t *features,
503 					 hb_set_t       *lookup_indexes /* OUT */)
504 {
505   _hb_ot_layout_collect_lookups_features (face,
506 					  table_tag,
507 					  script_index,
508 					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
509 					  features,
510 					  lookup_indexes);
511 
512   if (!languages)
513   {
514     /* All languages */
515     unsigned int count = hb_ot_layout_script_get_language_tags (face,
516 								table_tag,
517 								script_index,
518 								0, NULL, NULL);
519     for (unsigned int language_index = 0; language_index < count; language_index++)
520       _hb_ot_layout_collect_lookups_features (face,
521 					      table_tag,
522 					      script_index,
523 					      language_index,
524 					      features,
525 					      lookup_indexes);
526   }
527   else
528   {
529     for (; *languages; languages++)
530     {
531       unsigned int language_index;
532       if (hb_ot_layout_script_find_language (face,
533 					     table_tag,
534 					     script_index,
535 					     *languages,
536 					     &language_index))
537         _hb_ot_layout_collect_lookups_features (face,
538 						table_tag,
539 						script_index,
540 						language_index,
541 						features,
542 						lookup_indexes);
543     }
544   }
545 }
546 
547 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)548 hb_ot_layout_collect_lookups (hb_face_t      *face,
549 			      hb_tag_t        table_tag,
550 			      const hb_tag_t *scripts,
551 			      const hb_tag_t *languages,
552 			      const hb_tag_t *features,
553 			      hb_set_t       *lookup_indexes /* OUT */)
554 {
555   if (!scripts)
556   {
557     /* All scripts */
558     unsigned int count = hb_ot_layout_table_get_script_tags (face,
559 							     table_tag,
560 							     0, NULL, NULL);
561     for (unsigned int script_index = 0; script_index < count; script_index++)
562       _hb_ot_layout_collect_lookups_languages (face,
563 					       table_tag,
564 					       script_index,
565 					       languages,
566 					       features,
567 					       lookup_indexes);
568   }
569   else
570   {
571     for (; *scripts; scripts++)
572     {
573       unsigned int script_index;
574       if (hb_ot_layout_table_find_script (face,
575 					  table_tag,
576 					  *scripts,
577 					  &script_index))
578         _hb_ot_layout_collect_lookups_languages (face,
579 						 table_tag,
580 						 script_index,
581 						 languages,
582 						 features,
583 						 lookup_indexes);
584     }
585   }
586 }
587 
588 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)589 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
590 				    hb_tag_t      table_tag,
591 				    unsigned int  lookup_index,
592 				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
593 				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
594 				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
595 				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
596 {
597   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
598 
599   OT::hb_collect_glyphs_context_t c (face,
600 				     glyphs_before,
601 				     glyphs_input,
602 				     glyphs_after,
603 				     glyphs_output);
604 
605   switch (table_tag)
606   {
607     case HB_OT_TAG_GSUB:
608     {
609       const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
610       l.collect_glyphs_lookup (&c);
611       return;
612     }
613     case HB_OT_TAG_GPOS:
614     {
615       const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
616       l.collect_glyphs_lookup (&c);
617       return;
618     }
619   }
620 }
621 
622 
623 /*
624  * OT::GSUB
625  */
626 
627 hb_bool_t
hb_ot_layout_has_substitution(hb_face_t * face)628 hb_ot_layout_has_substitution (hb_face_t *face)
629 {
630   return &_get_gsub (face) != &OT::Null(OT::GSUB);
631 }
632 
633 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)634 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
635 				      unsigned int          lookup_index,
636 				      const hb_codepoint_t *glyphs,
637 				      unsigned int          glyphs_length,
638 				      hb_bool_t             zero_context)
639 {
640   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
641   return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
642 }
643 
644 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)645 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
646 					   unsigned int          lookup_index,
647 					   const hb_codepoint_t *glyphs,
648 					   unsigned int          glyphs_length,
649 					   hb_bool_t             zero_context)
650 {
651   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
652   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
653 
654   const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
655 
656   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
657 }
658 
659 void
hb_ot_layout_substitute_start(hb_font_t * font,hb_buffer_t * buffer)660 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
661 {
662   OT::GSUB::substitute_start (font, buffer);
663 }
664 
665 hb_bool_t
hb_ot_layout_substitute_lookup(hb_font_t * font,hb_buffer_t * buffer,unsigned int lookup_index,hb_mask_t mask,hb_bool_t auto_zwj)666 hb_ot_layout_substitute_lookup (hb_font_t    *font,
667 				hb_buffer_t  *buffer,
668 				unsigned int  lookup_index,
669 				hb_mask_t     mask,
670 				hb_bool_t     auto_zwj)
671 {
672   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
673 
674   OT::hb_apply_context_t c (0, font, buffer, mask, auto_zwj);
675 
676   const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index);
677 
678   return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
679 }
680 
681 void
hb_ot_layout_substitute_finish(hb_font_t * font,hb_buffer_t * buffer)682 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
683 {
684   OT::GSUB::substitute_finish (font, buffer);
685 }
686 
687 void
hb_ot_layout_lookup_substitute_closure(hb_face_t * face,unsigned int lookup_index,hb_set_t * glyphs)688 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
689 				        unsigned int  lookup_index,
690 				        hb_set_t     *glyphs)
691 {
692   OT::hb_closure_context_t c (face, glyphs);
693 
694   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
695 
696   l.closure (&c);
697 }
698 
699 /*
700  * OT::GPOS
701  */
702 
703 hb_bool_t
hb_ot_layout_has_positioning(hb_face_t * face)704 hb_ot_layout_has_positioning (hb_face_t *face)
705 {
706   return &_get_gpos (face) != &OT::Null(OT::GPOS);
707 }
708 
709 void
hb_ot_layout_position_start(hb_font_t * font,hb_buffer_t * buffer)710 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
711 {
712   OT::GPOS::position_start (font, buffer);
713 }
714 
715 hb_bool_t
hb_ot_layout_position_lookup(hb_font_t * font,hb_buffer_t * buffer,unsigned int lookup_index,hb_mask_t mask,hb_bool_t auto_zwj)716 hb_ot_layout_position_lookup (hb_font_t    *font,
717 			      hb_buffer_t  *buffer,
718 			      unsigned int  lookup_index,
719 			      hb_mask_t     mask,
720 			      hb_bool_t     auto_zwj)
721 {
722   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
723 
724   OT::hb_apply_context_t c (1, font, buffer, mask, auto_zwj);
725 
726   const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index);
727 
728   return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
729 }
730 
731 void
hb_ot_layout_position_finish(hb_font_t * font,hb_buffer_t * buffer)732 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
733 {
734   OT::GPOS::position_finish (font, buffer);
735 }
736 
737 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)738 hb_ot_layout_get_size_params (hb_face_t    *face,
739 			      unsigned int *design_size,       /* OUT.  May be NULL */
740 			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
741 			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
742 			      unsigned int *range_start,       /* OUT.  May be NULL */
743 			      unsigned int *range_end          /* OUT.  May be NULL */)
744 {
745   const OT::GPOS &gpos = _get_gpos (face);
746   const hb_tag_t tag = HB_TAG ('s','i','z','e');
747 
748   unsigned int num_features = gpos.get_feature_count ();
749   for (unsigned int i = 0; i < num_features; i++)
750   {
751     if (tag == gpos.get_feature_tag (i))
752     {
753       const OT::Feature &f = gpos.get_feature (i);
754       const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
755 
756       if (params.designSize)
757       {
758 #define PARAM(a, A) if (a) *a = params.A
759 	PARAM (design_size, designSize);
760 	PARAM (subfamily_id, subfamilyID);
761 	PARAM (subfamily_name_id, subfamilyNameID);
762 	PARAM (range_start, rangeStart);
763 	PARAM (range_end, rangeEnd);
764 #undef PARAM
765 
766 	return true;
767       }
768     }
769   }
770 
771 #define PARAM(a, A) if (a) *a = 0
772   PARAM (design_size, designSize);
773   PARAM (subfamily_id, subfamilyID);
774   PARAM (subfamily_name_id, subfamilyNameID);
775   PARAM (range_start, rangeStart);
776   PARAM (range_end, rangeEnd);
777 #undef PARAM
778 
779   return false;
780 }
781