hb-ot-layout.cc revision 23c86aa0009324433e78fcd0c47f2c0ff14b1949
1/*
2 * Copyright (C) 1998-2004  David Turner and Werner Lemberg
3 * Copyright (C) 2006  Behdad Esfahbod
4 * Copyright (C) 2007,2008,2009  Red Hat, Inc.
5 *
6 *  This is part of HarfBuzz, an OpenType Layout engine library.
7 *
8 * Permission is hereby granted, without written agreement and without
9 * license or royalty fees, to use, copy, modify, and distribute this
10 * software and its documentation for any purpose, provided that the
11 * above copyright notice and the following two paragraphs appear in
12 * all copies of this software.
13 *
14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * DAMAGE.
19 *
20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 *
26 * Red Hat Author(s): Behdad Esfahbod
27 */
28
29#define HB_OT_LAYOUT_CC
30
31#include "hb-ot-layout-private.h"
32
33#include "hb-ot-layout-gdef-private.hh"
34#include "hb-ot-layout-gsub-private.hh"
35#include "hb-ot-layout-gpos-private.hh"
36
37
38#include <stdlib.h>
39#include <string.h>
40
41
42void
43_hb_ot_layout_init (hb_ot_layout_t *layout)
44{
45  layout->gdef = NULL;
46  layout->gsub = NULL;
47  layout->gpos = NULL;
48}
49
50void
51_hb_ot_layout_fini (hb_ot_layout_t *layout)
52{
53}
54
55static hb_ot_layout_t *
56_hb_ot_face_get_layout (hb_face_t *face)
57{
58  return &face->ot_layout;
59}
60
61static const GDEF&
62_get_gdef (hb_face_t *face)
63{
64#if 0
65  if (HB_UNLIKELY (!layout->face))
66    return Null(GDEF);
67
68  if (HB_UNLIKELY (!layout->gdef)) {
69    hb_blob_t *blob = hb_face_get_table (face, HB_OT_TAG_GDEF);
70    unsigned int length;
71    const char *data = hb_blob_get_data (blob,
72  layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag)));
73    layout->gdef = &Null(GDEF);
74  }
75
76  return *layout->gdef;
77#endif
78}
79
80static const GSUB&
81_get_gsub (hb_face_t *face)
82{
83  return Null(GSUB);
84}
85
86static const GPOS&
87_get_gpos (hb_face_t *face)
88{
89  return Null(GPOS);
90}
91
92
93/*
94 * GDEF
95 */
96
97/* TODO the public class_t is a mess */
98
99hb_bool_t
100hb_ot_layout_has_font_glyph_classes (hb_face_t *face)
101{
102  return _get_gdef (face).has_glyph_classes ();
103}
104
105HB_INTERNAL hb_bool_t
106_hb_ot_layout_has_new_glyph_classes (hb_face_t *face)
107{
108  return face->ot_layout.new_gdef.len > 0;
109}
110
111static unsigned int
112_hb_ot_layout_get_glyph_property (hb_face_t      *face,
113				  hb_codepoint_t  glyph)
114{
115  hb_ot_layout_class_t klass;
116  const GDEF &gdef = _get_gdef (face);
117
118  klass = gdef.get_glyph_class (glyph);
119
120  if (!klass && glyph < face->ot_layout.new_gdef.len)
121    klass = face->ot_layout.new_gdef.klasses[glyph];
122
123  switch (klass) {
124  default:
125  case GDEF::UnclassifiedGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
126  case GDEF::BaseGlyph:		return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
127  case GDEF::LigatureGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
128  case GDEF::ComponentGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
129  case GDEF::MarkGlyph:
130	/* TODO old harfbuzz doesn't always parse mark attachments as it says it was
131	 * introduced without a version bump, so it may not be safe */
132	klass = gdef.get_mark_attachment_type (glyph);
133	return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8);
134  }
135}
136
137HB_INTERNAL hb_bool_t
138_hb_ot_layout_check_glyph_property (hb_face_t    *face,
139				    hb_internal_glyph_info_t *ginfo,
140				    unsigned int  lookup_flags,
141				    unsigned int *property_out)
142{
143  unsigned int property;
144
145  if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
146    ginfo->gproperty = _hb_ot_layout_get_glyph_property (face, ginfo->codepoint);
147  property = ginfo->gproperty;
148  if (property_out)
149    *property_out = property;
150
151  /* Not covered, if, for example, glyph class is ligature and
152   * lookup_flags includes LookupFlags::IgnoreLigatures
153   */
154  if (property & lookup_flags)
155    return false;
156
157  if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
158  {
159    /* If using mark filtering sets, the high short of
160     * lookup_flags has the set index.
161     */
162    if (lookup_flags & LookupFlag::UseMarkFilteringSet)
163      return _get_gdef (face).mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
164
165    /* The second byte of lookup_flags has the meaning
166     * "ignore marks of attachment type different than
167     * the attachment type specified."
168     */
169    if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
170      return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType);
171  }
172
173  return true;
174}
175
176HB_INTERNAL hb_bool_t
177_hb_ot_layout_skip_mark (hb_face_t    *face,
178			 hb_internal_glyph_info_t *ginfo,
179			 unsigned int  lookup_flags,
180			 unsigned int *property_out)
181{
182  unsigned int property;
183
184  if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
185    ginfo->gproperty = _hb_ot_layout_get_glyph_property (face, ginfo->codepoint);
186  property = ginfo->gproperty;
187  if (property_out)
188    *property_out = property;
189
190  if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
191  {
192    /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */
193    if (lookup_flags & LookupFlag::IgnoreMarks)
194      return true;
195
196    /* If using mark filtering sets, the high short of lookup_flags has the set index. */
197    if (lookup_flags & LookupFlag::UseMarkFilteringSet)
198      return !_get_gdef (face).mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
199
200    /* The second byte of lookup_flags has the meaning "ignore marks of attachment type
201     * different than the attachment type specified." */
202    if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
203      return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType);
204  }
205
206  return false;
207}
208
209HB_INTERNAL void
210_hb_ot_layout_set_glyph_class (hb_face_t                  *face,
211			       hb_codepoint_t              glyph,
212			       hb_ot_layout_glyph_class_t  klass)
213{
214  if (HB_OBJECT_IS_INERT (face))
215    return;
216
217  /* TODO optimize this? similar to old harfbuzz code for example */
218
219  hb_ot_layout_t *layout = &face->ot_layout;
220  hb_ot_layout_class_t gdef_klass;
221  int len = layout->new_gdef.len;
222
223  if (HB_UNLIKELY (glyph > 65535))
224    return;
225
226  /* XXX this is not threadsafe */
227  if (glyph >= len) {
228    int new_len;
229    unsigned char *new_klasses;
230
231    new_len = len == 0 ? 120 : 2 * len;
232    if (new_len > 65535)
233      new_len = 65535;
234    new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
235
236    if (HB_UNLIKELY (!new_klasses))
237      return;
238
239    memset (new_klasses + len, 0, new_len - len);
240
241    layout->new_gdef.klasses = new_klasses;
242    layout->new_gdef.len = new_len;
243  }
244
245  switch (klass) {
246  default:
247  case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED:	gdef_klass = GDEF::UnclassifiedGlyph;	break;
248  case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH:	gdef_klass = GDEF::BaseGlyph;		break;
249  case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE:	gdef_klass = GDEF::LigatureGlyph;	break;
250  case HB_OT_LAYOUT_GLYPH_CLASS_MARK:		gdef_klass = GDEF::MarkGlyph;		break;
251  case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT:	gdef_klass = GDEF::ComponentGlyph;	break;
252  }
253
254  layout->new_gdef.klasses[glyph] = gdef_klass;
255  return;
256}
257
258HB_INTERNAL void
259_hb_ot_layout_set_glyph_property (hb_face_t      *face,
260				  hb_codepoint_t  glyph,
261				  unsigned int    property)
262{ _hb_ot_layout_set_glyph_class (face, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); }
263
264
265hb_ot_layout_glyph_class_t
266hb_ot_layout_get_glyph_class (hb_face_t      *face,
267			      hb_codepoint_t  glyph)
268{
269  return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (face, glyph) & 0xff);
270}
271
272void
273hb_ot_layout_set_glyph_class (hb_face_t                 *face,
274			      hb_codepoint_t             glyph,
275			      hb_ot_layout_glyph_class_t klass)
276{
277  _hb_ot_layout_set_glyph_class (face, glyph, klass);
278}
279
280void
281hb_ot_layout_build_glyph_classes (hb_face_t      *face,
282				  uint16_t        num_total_glyphs,
283				  hb_codepoint_t *glyphs,
284				  unsigned char  *klasses,
285				  uint16_t        count)
286{
287  if (HB_OBJECT_IS_INERT (face))
288    return;
289
290  hb_ot_layout_t *layout = &face->ot_layout;
291
292  if (HB_UNLIKELY (!count || !glyphs || !klasses))
293    return;
294
295  if (layout->new_gdef.len == 0) {
296    layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
297    layout->new_gdef.len = count;
298  }
299
300  for (unsigned int i = 0; i < count; i++)
301    _hb_ot_layout_set_glyph_class (face, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
302}
303
304hb_bool_t
305hb_ot_layout_get_attach_points (hb_face_t      *face,
306				hb_codepoint_t  glyph,
307				unsigned int   *point_count /* IN/OUT */,
308				unsigned int   *point_array /* OUT */)
309{
310  return _get_gdef (face).get_attach_points (glyph, point_count, point_array);
311}
312
313hb_bool_t
314hb_ot_layout_get_lig_carets (hb_face_t      *face,
315			     hb_font_t      *font,
316			     hb_codepoint_t  glyph,
317			     unsigned int   *caret_count /* IN/OUT */,
318			     int            *caret_array /* OUT */)
319{
320  hb_ot_layout_context_t context;
321  context.font = font;
322  context.face = face;
323  return _get_gdef (face).get_lig_carets (&context, glyph, caret_count, caret_array);
324}
325
326/*
327 * GSUB/GPOS
328 */
329
330static const GSUBGPOS&
331get_gsubgpos_table (hb_face_t *face,
332		    hb_tag_t   table_tag)
333{
334  switch (table_tag) {
335    case HB_OT_TAG_GSUB: return _get_gsub (face);
336    case HB_OT_TAG_GPOS: return _get_gpos (face);
337    default:             return Null(GSUBGPOS);
338  }
339}
340
341
342unsigned int
343hb_ot_layout_table_get_script_count (hb_face_t *face,
344				     hb_tag_t   table_tag)
345{
346  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
347
348  return g.get_script_count ();
349}
350
351hb_tag_t
352hb_ot_layout_table_get_script_tag (hb_face_t    *face,
353				   hb_tag_t      table_tag,
354				   unsigned int  script_index)
355{
356  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
357
358  return g.get_script_tag (script_index);
359}
360
361hb_bool_t
362hb_ot_layout_table_find_script (hb_face_t    *face,
363				hb_tag_t      table_tag,
364				hb_tag_t      script_tag,
365				unsigned int *script_index)
366{
367  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
368  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
369
370  if (g.find_script_index (script_tag, script_index))
371    return TRUE;
372
373  /* try finding 'DFLT' */
374  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
375    return FALSE;
376
377  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
378  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
379    return FALSE;
380
381  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
382  return FALSE;
383}
384
385unsigned int
386hb_ot_layout_table_get_feature_count (hb_face_t *face,
387				      hb_tag_t   table_tag)
388{
389  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
390
391  return g.get_feature_count ();
392}
393
394hb_tag_t
395hb_ot_layout_table_get_feature_tag (hb_face_t    *face,
396				    hb_tag_t      table_tag,
397				    unsigned int  feature_index)
398{
399  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
400
401  return g.get_feature_tag (feature_index);
402}
403
404hb_bool_t
405hb_ot_layout_table_find_feature (hb_face_t    *face,
406				 hb_tag_t      table_tag,
407				 hb_tag_t      feature_tag,
408				 unsigned int *feature_index)
409{
410  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
411  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
412
413  if (g.find_feature_index (feature_tag, feature_index))
414    return TRUE;
415
416  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
417  return FALSE;
418}
419
420unsigned int
421hb_ot_layout_table_get_lookup_count (hb_face_t *face,
422				     hb_tag_t   table_tag)
423{
424  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
425
426  return g.get_lookup_count ();
427}
428
429
430unsigned int
431hb_ot_layout_script_get_language_count (hb_face_t    *face,
432					hb_tag_t      table_tag,
433					unsigned int  script_index)
434{
435  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
436
437  return s.get_lang_sys_count ();
438}
439
440hb_tag_t
441hb_ot_layout_script_get_language_tag (hb_face_t    *face,
442				      hb_tag_t      table_tag,
443				      unsigned int  script_index,
444				      unsigned int  language_index)
445{
446  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
447
448  return s.get_lang_sys_tag (language_index);
449}
450
451hb_bool_t
452hb_ot_layout_script_find_language (hb_face_t    *face,
453				   hb_tag_t      table_tag,
454				   unsigned int  script_index,
455				   hb_tag_t      language_tag,
456				   unsigned int *language_index)
457{
458  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
459  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
460
461  if (s.find_lang_sys_index (language_tag, language_index))
462    return TRUE;
463
464  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
465  if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
466    return FALSE;
467
468  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
469  return FALSE;
470}
471
472hb_bool_t
473hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
474						  hb_tag_t      table_tag,
475						  unsigned int  script_index,
476						  unsigned int  language_index,
477						  unsigned int *feature_index)
478{
479  const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
480
481  if (feature_index) *feature_index = l.get_required_feature_index ();
482
483  return l.has_required_feature ();
484}
485
486unsigned int
487hb_ot_layout_language_get_feature_count (hb_face_t    *face,
488					 hb_tag_t      table_tag,
489					 unsigned int  script_index,
490					 unsigned int  language_index)
491{
492  const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
493
494  return l.get_feature_count ();
495}
496
497unsigned int
498hb_ot_layout_language_get_feature_index (hb_face_t    *face,
499					 hb_tag_t      table_tag,
500					 unsigned int  script_index,
501					 unsigned int  language_index,
502					 unsigned int  num_feature)
503{
504  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
505  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
506
507  return l.get_feature_index (num_feature);
508}
509
510hb_tag_t
511hb_ot_layout_language_get_feature_tag (hb_face_t    *face,
512				       hb_tag_t      table_tag,
513				       unsigned int  script_index,
514				       unsigned int  language_index,
515				       unsigned int  num_feature)
516{
517  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
518  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
519  unsigned int feature_index = l.get_feature_index (num_feature);
520
521  return g.get_feature_tag (feature_index);
522}
523
524
525hb_bool_t
526hb_ot_layout_language_find_feature (hb_face_t    *face,
527				    hb_tag_t      table_tag,
528				    unsigned int  script_index,
529				    unsigned int  language_index,
530				    hb_tag_t      feature_tag,
531				    unsigned int *feature_index)
532{
533  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
534  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
535  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
536
537  unsigned int num_features = l.get_feature_count ();
538  for (unsigned int i = 0; i < num_features; i++) {
539    unsigned int f_index = l.get_feature_index (i);
540
541    if (feature_tag == g.get_feature_tag (f_index)) {
542      if (feature_index) *feature_index = f_index;
543      return TRUE;
544    }
545  }
546
547  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
548  return FALSE;
549}
550
551unsigned int
552hb_ot_layout_feature_get_lookup_count (hb_face_t    *face,
553				       hb_tag_t      table_tag,
554				       unsigned int  feature_index)
555{
556  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
557  const Feature &f = g.get_feature (feature_index);
558
559  return f.get_lookup_count ();
560}
561
562unsigned int
563hb_ot_layout_feature_get_lookup_index (hb_face_t    *face,
564				       hb_tag_t      table_tag,
565				       unsigned int  feature_index,
566				       unsigned int  num_lookup)
567{
568  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
569  const Feature &f = g.get_feature (feature_index);
570
571  return f.get_lookup_index (num_lookup);
572}
573
574/*
575 * GSUB
576 */
577
578hb_bool_t
579hb_ot_layout_has_substitution (hb_face_t *face)
580{
581  return &_get_gsub (face) != &Null(GSUB);
582}
583
584hb_bool_t
585hb_ot_layout_substitute_lookup (hb_face_t                   *face,
586				hb_buffer_t                 *buffer,
587				unsigned int                 lookup_index,
588				hb_ot_layout_feature_mask_t  mask)
589{
590  hb_ot_layout_context_t context;
591  context.font = NULL;
592  context.face = face;
593  return _get_gsub (face).substitute_lookup (&context, buffer, lookup_index, mask);
594}
595
596/*
597 * GPOS
598 */
599
600hb_bool_t
601hb_ot_layout_has_positioning (hb_face_t *face)
602{
603  return &_get_gpos (face) != &Null(GPOS);
604}
605
606hb_bool_t
607hb_ot_layout_position_lookup   (hb_face_t                   *face,
608				hb_font_t                   *font,
609				hb_buffer_t                 *buffer,
610				unsigned int                 lookup_index,
611				hb_ot_layout_feature_mask_t  mask)
612{
613  hb_ot_layout_context_t context;
614  context.font = font;
615  context.face = face;
616  return _get_gpos (face).position_lookup (&context, buffer, lookup_index, mask);
617}
618