hb-ot-layout.cc revision fd42257f8c45ff8e036e1c3eb1a788a101be7ead
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#include "hb-ot-maxp-table.hh"
37
38
39#include <stdlib.h>
40#include <string.h>
41
42
43HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
44
45static hb_bool_t
46hb_ot_layout_ensure (hb_face_t *face)
47{
48  return hb_ot_shaper_face_data_ensure (face);
49}
50
51
52hb_ot_layout_t *
53_hb_ot_layout_create (hb_face_t *face)
54{
55  hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
56
57  layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF));
58  layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
59
60  layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB));
61  layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
62
63  layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
64  layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
65
66  return layout;
67}
68
69void
70_hb_ot_layout_destroy (hb_ot_layout_t *layout)
71{
72  hb_blob_destroy (layout->gdef_blob);
73  hb_blob_destroy (layout->gsub_blob);
74  hb_blob_destroy (layout->gpos_blob);
75
76  free (layout);
77}
78
79static inline const GDEF&
80_get_gdef (hb_face_t *face)
81{
82  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GDEF);
83  return *hb_ot_layout_from_face (face)->gdef;
84}
85static inline const GSUB&
86_get_gsub (hb_face_t *face)
87{
88  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GSUB);
89  return *hb_ot_layout_from_face (face)->gsub;
90}
91static inline const GPOS&
92_get_gpos (hb_face_t *face)
93{
94  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GPOS);
95  return *hb_ot_layout_from_face (face)->gpos;
96}
97
98
99/*
100 * GDEF
101 */
102
103hb_bool_t
104hb_ot_layout_has_glyph_classes (hb_face_t *face)
105{
106  return _get_gdef (face).has_glyph_classes ();
107}
108
109static inline unsigned int
110_hb_ot_layout_get_glyph_property (hb_face_t       *face,
111				  hb_glyph_info_t *info)
112{
113  if (!info->props_cache())
114  {
115    info->props_cache() = hb_ot_layout_from_face (face)->gdef->get_glyph_props (info->codepoint);
116  }
117
118  return info->props_cache();
119}
120
121static inline hb_bool_t
122_hb_ot_layout_match_properties_mark (hb_face_t      *face,
123				     hb_codepoint_t  glyph,
124				     unsigned int    glyph_props,
125				     unsigned int    lookup_props)
126{
127  /* If using mark filtering sets, the high short of
128   * lookup_props has the set index.
129   */
130  if (lookup_props & LookupFlag::UseMarkFilteringSet)
131    return hb_ot_layout_from_face (face)->gdef->mark_set_covers (lookup_props >> 16, glyph);
132
133  /* The second byte of lookup_props has the meaning
134   * "ignore marks of attachment type different than
135   * the attachment type specified."
136   */
137  if (lookup_props & LookupFlag::MarkAttachmentType)
138    return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
139
140  return true;
141}
142
143static inline hb_bool_t
144_hb_ot_layout_match_properties (hb_face_t      *face,
145				hb_codepoint_t  glyph,
146				unsigned int    glyph_props,
147				unsigned int    lookup_props)
148{
149  /* Not covered, if, for example, glyph class is ligature and
150   * lookup_props includes LookupFlags::IgnoreLigatures
151   */
152  if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
153    return false;
154
155  if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
156    return _hb_ot_layout_match_properties_mark (face, glyph, glyph_props, lookup_props);
157
158  return true;
159}
160
161hb_bool_t
162_hb_ot_layout_check_glyph_property (hb_face_t    *face,
163				    hb_glyph_info_t *ginfo,
164				    unsigned int  lookup_props,
165				    unsigned int *property_out)
166{
167  unsigned int property;
168
169  property = _hb_ot_layout_get_glyph_property (face, ginfo);
170  *property_out = property;
171
172  return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
173}
174
175hb_bool_t
176_hb_ot_layout_skip_mark (hb_face_t    *face,
177			 hb_glyph_info_t *ginfo,
178			 unsigned int  lookup_props,
179			 unsigned int *property_out)
180{
181  unsigned int property;
182
183  property = _hb_ot_layout_get_glyph_property (face, ginfo);
184  if (property_out)
185    *property_out = property;
186
187  /* If it's a mark, skip it if we don't accept it. */
188  if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
189    return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
190
191  /* If not a mark, don't skip. */
192  return false;
193}
194
195
196
197unsigned int
198hb_ot_layout_get_attach_points (hb_face_t      *face,
199				hb_codepoint_t  glyph,
200				unsigned int    start_offset,
201				unsigned int   *point_count /* IN/OUT */,
202				unsigned int   *point_array /* OUT */)
203{
204  return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
205}
206
207unsigned int
208hb_ot_layout_get_ligature_carets (hb_font_t      *font,
209				  hb_direction_t  direction,
210				  hb_codepoint_t  glyph,
211				  unsigned int    start_offset,
212				  unsigned int   *caret_count /* IN/OUT */,
213				  int            *caret_array /* OUT */)
214{
215  return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
216}
217
218/*
219 * GSUB/GPOS
220 */
221
222static const GSUBGPOS&
223get_gsubgpos_table (hb_face_t *face,
224		    hb_tag_t   table_tag)
225{
226  switch (table_tag) {
227    case HB_OT_TAG_GSUB: return _get_gsub (face);
228    case HB_OT_TAG_GPOS: return _get_gpos (face);
229    default:             return Null(GSUBGPOS);
230  }
231}
232
233
234unsigned int
235hb_ot_layout_table_get_script_tags (hb_face_t    *face,
236				    hb_tag_t      table_tag,
237				    unsigned int  start_offset,
238				    unsigned int *script_count /* IN/OUT */,
239				    hb_tag_t     *script_tags /* OUT */)
240{
241  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
242
243  return g.get_script_tags (start_offset, script_count, script_tags);
244}
245
246hb_bool_t
247hb_ot_layout_table_find_script (hb_face_t    *face,
248				hb_tag_t      table_tag,
249				hb_tag_t      script_tag,
250				unsigned int *script_index)
251{
252  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
253  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
254
255  if (g.find_script_index (script_tag, script_index))
256    return true;
257
258  /* try finding 'DFLT' */
259  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
260    return false;
261
262  /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
263   * including many versions of DejaVu Sans Mono! */
264  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
265    return false;
266
267  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
268  return false;
269}
270
271hb_bool_t
272hb_ot_layout_table_choose_script (hb_face_t      *face,
273				  hb_tag_t        table_tag,
274				  const hb_tag_t *script_tags,
275				  unsigned int   *script_index,
276				  hb_tag_t       *chosen_script)
277{
278  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
279  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
280
281  while (*script_tags)
282  {
283    if (g.find_script_index (*script_tags, script_index)) {
284      if (chosen_script)
285        *chosen_script = *script_tags;
286      return true;
287    }
288    script_tags++;
289  }
290
291  /* try finding 'DFLT' */
292  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
293    if (chosen_script)
294      *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
295    return false;
296  }
297
298  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
299  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
300    if (chosen_script)
301      *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
302    return false;
303  }
304
305  /* try with 'latn'; some old fonts put their features there even though
306     they're really trying to support Thai, for example :( */
307#define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
308  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
309    if (chosen_script)
310      *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
311    return false;
312  }
313
314  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
315  if (chosen_script)
316    *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
317  return false;
318}
319
320unsigned int
321hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
322				     hb_tag_t      table_tag,
323				     unsigned int  start_offset,
324				     unsigned int *feature_count /* IN/OUT */,
325				     hb_tag_t     *feature_tags /* OUT */)
326{
327  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
328
329  return g.get_feature_tags (start_offset, feature_count, feature_tags);
330}
331
332
333unsigned int
334hb_ot_layout_script_get_language_tags (hb_face_t    *face,
335				       hb_tag_t      table_tag,
336				       unsigned int  script_index,
337				       unsigned int  start_offset,
338				       unsigned int *language_count /* IN/OUT */,
339				       hb_tag_t     *language_tags /* OUT */)
340{
341  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
342
343  return s.get_lang_sys_tags (start_offset, language_count, language_tags);
344}
345
346hb_bool_t
347hb_ot_layout_script_find_language (hb_face_t    *face,
348				   hb_tag_t      table_tag,
349				   unsigned int  script_index,
350				   hb_tag_t      language_tag,
351				   unsigned int *language_index)
352{
353  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
354  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
355
356  if (s.find_lang_sys_index (language_tag, language_index))
357    return true;
358
359  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
360  if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
361    return false;
362
363  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
364  return false;
365}
366
367hb_bool_t
368hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
369						  hb_tag_t      table_tag,
370						  unsigned int  script_index,
371						  unsigned int  language_index,
372						  unsigned int *feature_index)
373{
374  const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
375
376  if (feature_index) *feature_index = l.get_required_feature_index ();
377
378  return l.has_required_feature ();
379}
380
381unsigned int
382hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
383					   hb_tag_t      table_tag,
384					   unsigned int  script_index,
385					   unsigned int  language_index,
386					   unsigned int  start_offset,
387					   unsigned int *feature_count /* IN/OUT */,
388					   unsigned int *feature_indexes /* OUT */)
389{
390  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
391  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
392
393  return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
394}
395
396unsigned int
397hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
398					hb_tag_t      table_tag,
399					unsigned int  script_index,
400					unsigned int  language_index,
401					unsigned int  start_offset,
402					unsigned int *feature_count /* IN/OUT */,
403					hb_tag_t     *feature_tags /* OUT */)
404{
405  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
406  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
407
408  ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
409  unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
410
411  if (feature_tags) {
412    unsigned int count = *feature_count;
413    for (unsigned int i = 0; i < count; i++)
414      feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
415  }
416
417  return ret;
418}
419
420
421hb_bool_t
422hb_ot_layout_language_find_feature (hb_face_t    *face,
423				    hb_tag_t      table_tag,
424				    unsigned int  script_index,
425				    unsigned int  language_index,
426				    hb_tag_t      feature_tag,
427				    unsigned int *feature_index)
428{
429  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
430  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
431  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
432
433  unsigned int num_features = l.get_feature_count ();
434  for (unsigned int i = 0; i < num_features; i++) {
435    unsigned int f_index = l.get_feature_index (i);
436
437    if (feature_tag == g.get_feature_tag (f_index)) {
438      if (feature_index) *feature_index = f_index;
439      return true;
440    }
441  }
442
443  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
444  return false;
445}
446
447unsigned int
448hb_ot_layout_feature_get_lookup_indexes (hb_face_t    *face,
449					 hb_tag_t      table_tag,
450					 unsigned int  feature_index,
451					 unsigned int  start_offset,
452					 unsigned int *lookup_count /* IN/OUT */,
453					 unsigned int *lookup_indexes /* OUT */)
454{
455  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
456  const Feature &f = g.get_feature (feature_index);
457
458  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
459}
460
461
462/*
463 * GSUB
464 */
465
466hb_bool_t
467hb_ot_layout_has_substitution (hb_face_t *face)
468{
469  return &_get_gsub (face) != &Null(GSUB);
470}
471
472hb_bool_t
473hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
474				      const hb_codepoint_t *glyphs,
475				      unsigned int          glyphs_length,
476				      unsigned int          lookup_index)
477{
478  if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
479  hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1);
480  return _get_gsub (face).would_substitute_lookup (&c, lookup_index);
481}
482
483hb_bool_t
484hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
485					   const hb_codepoint_t *glyphs,
486					   unsigned int          glyphs_length,
487					   unsigned int          lookup_index)
488{
489  if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
490  hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1);
491  return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
492}
493
494void
495hb_ot_layout_substitute_start (hb_buffer_t  *buffer)
496{
497  GSUB::substitute_start (buffer);
498}
499
500hb_bool_t
501hb_ot_layout_substitute_lookup (hb_face_t    *face,
502				hb_buffer_t  *buffer,
503				unsigned int  lookup_index,
504				hb_mask_t     mask)
505{
506  hb_apply_context_t c (NULL, face, buffer, mask);
507  return _get_gsub (face).substitute_lookup (&c, lookup_index);
508}
509
510hb_bool_t
511hb_ot_layout_substitute_lookup_fast (hb_face_t    *face,
512				     hb_buffer_t  *buffer,
513				     unsigned int  lookup_index,
514				     hb_mask_t     mask)
515{
516  hb_apply_context_t c (NULL, face, buffer, mask);
517  return hb_ot_layout_from_face (face)->gsub->substitute_lookup (&c, lookup_index);
518}
519
520void
521hb_ot_layout_substitute_finish (hb_buffer_t  *buffer HB_UNUSED)
522{
523  GSUB::substitute_finish (buffer);
524}
525
526void
527hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
528				        hb_set_t     *glyphs,
529				        unsigned int  lookup_index)
530{
531  hb_closure_context_t c (face, glyphs);
532  _get_gsub (face).closure_lookup (&c, lookup_index);
533}
534
535/*
536 * GPOS
537 */
538
539hb_bool_t
540hb_ot_layout_has_positioning (hb_face_t *face)
541{
542  return &_get_gpos (face) != &Null(GPOS);
543}
544
545void
546hb_ot_layout_position_start (hb_buffer_t  *buffer)
547{
548  GPOS::position_start (buffer);
549}
550
551hb_bool_t
552hb_ot_layout_position_lookup (hb_font_t    *font,
553			      hb_buffer_t  *buffer,
554			      unsigned int  lookup_index,
555			      hb_mask_t     mask)
556{
557  hb_apply_context_t c (font, font->face, buffer, mask);
558  return _get_gpos (font->face).position_lookup (&c, lookup_index);
559}
560
561hb_bool_t
562hb_ot_layout_position_lookup_fast (hb_font_t    *font,
563				   hb_buffer_t  *buffer,
564				   unsigned int  lookup_index,
565				   hb_mask_t     mask)
566{
567  hb_apply_context_t c (font, font->face, buffer, mask);
568  return hb_ot_layout_from_face (font->face)->gpos->position_lookup (&c, lookup_index);
569}
570
571void
572hb_ot_layout_position_finish (hb_buffer_t  *buffer)
573{
574  GPOS::position_finish (buffer);
575}
576
577
578