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