hb-ot-layout.cc revision 4189b92aaa41e4a1756c561cc6e08b0ed024afda
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/* XXX */
32#include "hb-buffer-private.h"
33
34#include "hb-ot-layout.h"
35#include "hb-ot-layout-private.h"
36
37#include "hb-ot-layout-open-private.h"
38#include "hb-ot-layout-gdef-private.h"
39#include "hb-ot-layout-gsub-private.h"
40#include "hb-ot-layout-gpos-private.h"
41
42
43#include <stdlib.h>
44#include <string.h>
45
46
47hb_ot_layout_t *
48hb_ot_layout_create (void)
49{
50  hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
51
52  layout->gdef = &Null(GDEF);
53  layout->gsub = &Null(GSUB);
54  layout->gpos = &Null(GPOS);
55
56  return layout;
57}
58
59hb_ot_layout_t *
60hb_ot_layout_create_for_data (const char *font_data,
61			      int         face_index)
62{
63  hb_ot_layout_t *layout;
64
65  if (HB_UNLIKELY (font_data == NULL))
66    return hb_ot_layout_create ();
67
68  layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
69
70  const OpenTypeFontFile &font = OpenTypeFontFile::get_for_data (font_data);
71  const OpenTypeFontFace &face = font.get_face (face_index);
72
73  layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag)));
74  layout->gsub = &GSUB::get_for_data (font.get_table_data (face.get_table_by_tag (GSUB::Tag)));
75  layout->gpos = &GPOS::get_for_data (font.get_table_data (face.get_table_by_tag (GPOS::Tag)));
76
77  return layout;
78}
79
80void
81hb_ot_layout_destroy (hb_ot_layout_t *layout)
82{
83  free (layout);
84}
85
86void
87hb_ot_layout_set_direction (hb_ot_layout_t *layout,
88			    hb_bool_t r2l)
89{
90  layout->gpos_info.r2l = !!r2l;
91}
92
93void
94hb_ot_layout_set_hinting (hb_ot_layout_t *layout,
95			  hb_bool_t hinted)
96{
97  layout->gpos_info.dvi = !hinted;
98}
99
100void
101hb_ot_layout_set_scale (hb_ot_layout_t *layout,
102			hb_16dot16_t x_scale, hb_16dot16_t y_scale)
103{
104  layout->gpos_info.x_scale = x_scale;
105  layout->gpos_info.y_scale = y_scale;
106}
107
108void
109hb_ot_layout_set_ppem (hb_ot_layout_t *layout,
110		       unsigned int x_ppem, unsigned int y_ppem)
111{
112  layout->gpos_info.x_ppem = x_ppem;
113  layout->gpos_info.y_ppem = y_ppem;
114}
115
116
117/*
118 * GDEF
119 */
120
121/* TODO the public class_t is a mess */
122
123hb_bool_t
124hb_ot_layout_has_font_glyph_classes (hb_ot_layout_t *layout)
125{
126  return layout->gdef->has_glyph_classes ();
127}
128
129HB_INTERNAL hb_bool_t
130_hb_ot_layout_has_new_glyph_classes (hb_ot_layout_t *layout)
131{
132  return layout->new_gdef.len > 0;
133}
134
135HB_INTERNAL unsigned int
136_hb_ot_layout_get_glyph_property (hb_ot_layout_t *layout,
137				  hb_codepoint_t  glyph)
138{
139  hb_ot_layout_class_t klass;
140
141  klass = layout->gdef->get_glyph_class (glyph);
142
143  if (!klass && glyph < layout->new_gdef.len)
144    klass = layout->new_gdef.klasses[glyph];
145
146  switch (klass) {
147  default:
148  case GDEF::UnclassifiedGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
149  case GDEF::BaseGlyph:		return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
150  case GDEF::LigatureGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
151  case GDEF::ComponentGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
152  case GDEF::MarkGlyph:
153	/* TODO old harfbuzz doesn't always parse mark attachments as it says it was
154	 * introduced without a version bump, so it may not be safe */
155	klass = layout->gdef->get_mark_attachment_type (glyph);
156	return HB_OT_LAYOUT_GLYPH_CLASS_MARK + klass << 8;
157  }
158}
159
160HB_INTERNAL hb_bool_t
161_hb_ot_layout_check_glyph_property (hb_ot_layout_t  *layout,
162				    hb_glyph_info_t *ginfo,
163				    unsigned int     lookup_flags,
164				    unsigned int    *property_out)
165{
166  unsigned int property;
167
168  if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
169    ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->gindex);
170  property = ginfo->gproperty;
171  if (property_out)
172    *property_out = property;
173
174  /* Not covered, if, for example, glyph class is ligature and
175   * lookup_flags includes LookupFlags::IgnoreLigatures
176   */
177  if (property & lookup_flags)
178    return false;
179
180  if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
181  {
182    /* If using mark filtering sets, the high short of
183     * lookup_flags has the set index.
184     */
185    if (lookup_flags & LookupFlag::UseMarkFilteringSet)
186      return layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->gindex);
187
188    /* The second byte of lookup_flags has the meaning
189     * "ignore marks of attachment type different than
190     * the attachment type specified."
191     */
192    if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
193      return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType);
194  }
195
196  return true;
197}
198
199HB_INTERNAL hb_bool_t
200_hb_ot_layout_skip_mark (hb_ot_layout_t  *layout,
201			 hb_glyph_info_t *ginfo,
202			 unsigned int     lookup_flags,
203			 unsigned int    *property_out)
204{
205  unsigned int property;
206
207  if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
208    ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->gindex);
209  property = ginfo->gproperty;
210  if (property_out)
211    *property_out = property;
212
213  if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
214  {
215    /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */
216    if (lookup_flags & LookupFlag::IgnoreMarks)
217      return true;
218
219    /* If using mark filtering sets, the high short of lookup_flags has the set index. */
220    if (lookup_flags & LookupFlag::UseMarkFilteringSet)
221      return !layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->gindex);
222
223    /* The second byte of lookup_flags has the meaning "ignore marks of attachment type
224     * different than the attachment type specified." */
225    if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
226      return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType);
227  }
228
229  return false;
230}
231
232HB_INTERNAL void
233_hb_ot_layout_set_glyph_property (hb_ot_layout_t *layout,
234				  hb_codepoint_t  glyph,
235				  unsigned int    property)
236{ hb_ot_layout_set_glyph_class (layout, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); }
237
238
239hb_ot_layout_glyph_class_t
240hb_ot_layout_get_glyph_class (hb_ot_layout_t *layout,
241			      hb_codepoint_t  glyph)
242{ return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (layout, glyph) & 0xff); }
243
244void
245hb_ot_layout_set_glyph_class (hb_ot_layout_t             *layout,
246			      hb_codepoint_t              glyph,
247			      hb_ot_layout_glyph_class_t  klass)
248{
249  /* TODO optimize this, similar to old harfbuzz code for example */
250
251  hb_ot_layout_class_t gdef_klass;
252  int len = layout->new_gdef.len;
253
254  if (HB_UNLIKELY (glyph > 65535))
255    return;
256
257  if (glyph >= len) {
258    int new_len;
259    unsigned char *new_klasses;
260
261    new_len = len == 0 ? 120 : 2 * len;
262    if (new_len > 65535)
263      new_len = 65535;
264    new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
265
266    if (HB_UNLIKELY (!new_klasses))
267      return;
268
269    memset (new_klasses + len, 0, new_len - len);
270
271    layout->new_gdef.klasses = new_klasses;
272    layout->new_gdef.len = new_len;
273  }
274
275  switch (klass) {
276  default:
277  case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED:	gdef_klass = GDEF::UnclassifiedGlyph;	break;
278  case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH:	gdef_klass = GDEF::BaseGlyph;		break;
279  case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE:	gdef_klass = GDEF::LigatureGlyph;	break;
280  case HB_OT_LAYOUT_GLYPH_CLASS_MARK:		gdef_klass = GDEF::MarkGlyph;		break;
281  case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT:	gdef_klass = GDEF::ComponentGlyph;	break;
282  }
283
284  layout->new_gdef.klasses[glyph] = gdef_klass;
285  return;
286}
287
288void
289hb_ot_layout_build_glyph_classes (hb_ot_layout_t *layout,
290				  uint16_t        num_total_glyphs,
291				  hb_codepoint_t *glyphs,
292				  unsigned char  *klasses,
293				  uint16_t        count)
294{
295  if (HB_UNLIKELY (!count || !glyphs || !klasses))
296    return;
297
298  if (layout->new_gdef.len == 0) {
299    layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
300    layout->new_gdef.len = count;
301  }
302
303  for (unsigned int i = 0; i < count; i++)
304    hb_ot_layout_set_glyph_class (layout, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
305}
306
307hb_bool_t
308hb_ot_layout_get_attach_points (hb_ot_layout_t *layout,
309				hb_codepoint_t  glyph,
310				unsigned int   *point_count /* IN/OUT */,
311				unsigned int   *point_array /* OUT */)
312{
313  return layout->gdef->get_attach_points (glyph, point_count, point_array);
314}
315
316hb_bool_t
317hb_ot_layout_get_lig_carets (hb_ot_layout_t *layout,
318			     hb_codepoint_t  glyph,
319			     unsigned int   *caret_count /* IN/OUT */,
320			     int            *caret_array /* OUT */)
321{
322  return layout->gdef->get_lig_carets (layout, glyph, caret_count, caret_array);
323}
324
325/*
326 * GSUB/GPOS
327 */
328
329static const GSUBGPOS&
330get_gsubgpos_table (hb_ot_layout_t            *layout,
331		    hb_ot_layout_table_type_t  table_type)
332{
333  switch (table_type) {
334    case HB_OT_LAYOUT_TABLE_TYPE_GSUB: return *(layout->gsub);
335    case HB_OT_LAYOUT_TABLE_TYPE_GPOS: return *(layout->gpos);
336    default:                           return Null(GSUBGPOS);
337  }
338}
339
340
341unsigned int
342hb_ot_layout_table_get_script_count (hb_ot_layout_t            *layout,
343				     hb_ot_layout_table_type_t  table_type)
344{
345  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
346
347  return g.get_script_count ();
348}
349
350hb_tag_t
351hb_ot_layout_table_get_script_tag (hb_ot_layout_t            *layout,
352				   hb_ot_layout_table_type_t  table_type,
353				   unsigned int               script_index)
354{
355  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
356
357  return g.get_script_tag (script_index);
358}
359
360hb_bool_t
361hb_ot_layout_table_find_script (hb_ot_layout_t            *layout,
362				hb_ot_layout_table_type_t  table_type,
363				hb_tag_t                   script_tag,
364				unsigned int              *script_index)
365{
366  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
367  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
368
369  if (g.find_script_index (script_tag, script_index))
370    return TRUE;
371
372  /* try finding 'DFLT' */
373  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
374    return FALSE;
375
376  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
377  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
378    return FALSE;
379
380  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
381  return FALSE;
382}
383
384unsigned int
385hb_ot_layout_table_get_feature_count (hb_ot_layout_t            *layout,
386				      hb_ot_layout_table_type_t  table_type)
387{
388  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
389
390  return g.get_feature_count ();
391}
392
393hb_tag_t
394hb_ot_layout_table_get_feature_tag (hb_ot_layout_t            *layout,
395				    hb_ot_layout_table_type_t  table_type,
396				    unsigned int               feature_index)
397{
398  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
399
400  return g.get_feature_tag (feature_index);
401}
402
403hb_bool_t
404hb_ot_layout_table_find_feature (hb_ot_layout_t            *layout,
405				 hb_ot_layout_table_type_t  table_type,
406				 hb_tag_t                   feature_tag,
407				 unsigned int              *feature_index)
408{
409  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
410  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
411
412  if (g.find_feature_index (feature_tag, feature_index))
413    return TRUE;
414
415  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
416  return FALSE;
417}
418
419unsigned int
420hb_ot_layout_table_get_lookup_count (hb_ot_layout_t            *layout,
421				     hb_ot_layout_table_type_t  table_type)
422{
423  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
424
425  return g.get_lookup_count ();
426}
427
428
429unsigned int
430hb_ot_layout_script_get_language_count (hb_ot_layout_t            *layout,
431					hb_ot_layout_table_type_t  table_type,
432					unsigned int               script_index)
433{
434  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
435
436  return s.get_lang_sys_count ();
437}
438
439hb_tag_t
440hb_ot_layout_script_get_language_tag (hb_ot_layout_t            *layout,
441				      hb_ot_layout_table_type_t  table_type,
442				      unsigned int               script_index,
443				      unsigned int               language_index)
444{
445  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
446
447  return s.get_lang_sys_tag (language_index);
448}
449
450hb_bool_t
451hb_ot_layout_script_find_language (hb_ot_layout_t            *layout,
452				   hb_ot_layout_table_type_t  table_type,
453				   unsigned int               script_index,
454				   hb_tag_t                   language_tag,
455				   unsigned int              *language_index)
456{
457  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
458  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
459
460  if (s.find_lang_sys_index (language_tag, language_index))
461    return TRUE;
462
463  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
464  if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
465    return FALSE;
466
467  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
468  return FALSE;
469}
470
471hb_bool_t
472hb_ot_layout_language_get_required_feature_index (hb_ot_layout_t            *layout,
473						  hb_ot_layout_table_type_t  table_type,
474						  unsigned int               script_index,
475						  unsigned int               language_index,
476						  unsigned int              *feature_index)
477{
478  const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
479
480  if (feature_index) *feature_index = l.get_required_feature_index ();
481
482  return l.has_required_feature ();
483}
484
485unsigned int
486hb_ot_layout_language_get_feature_count (hb_ot_layout_t            *layout,
487					 hb_ot_layout_table_type_t  table_type,
488					 unsigned int               script_index,
489					 unsigned int               language_index)
490{
491  const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
492
493  return l.get_feature_count ();
494}
495
496unsigned int
497hb_ot_layout_language_get_feature_index (hb_ot_layout_t            *layout,
498					 hb_ot_layout_table_type_t  table_type,
499					 unsigned int               script_index,
500					 unsigned int               language_index,
501					 unsigned int               num_feature)
502{
503  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
504  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
505
506  return l.get_feature_index (num_feature);
507}
508
509hb_tag_t
510hb_ot_layout_language_get_feature_tag (hb_ot_layout_t            *layout,
511				       hb_ot_layout_table_type_t  table_type,
512				       unsigned int               script_index,
513				       unsigned int               language_index,
514				       unsigned int               num_feature)
515{
516  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
517  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
518  unsigned int feature_index = l.get_feature_index (num_feature);
519
520  return g.get_feature_tag (feature_index);
521}
522
523
524hb_bool_t
525hb_ot_layout_language_find_feature (hb_ot_layout_t            *layout,
526				    hb_ot_layout_table_type_t  table_type,
527				    unsigned int               script_index,
528				    unsigned int               language_index,
529				    hb_tag_t                   feature_tag,
530				    unsigned int              *feature_index)
531{
532  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
533  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
534  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
535
536  unsigned int num_features = l.get_feature_count ();
537  for (unsigned int i = 0; i < num_features; i++) {
538    unsigned int f_index = l.get_feature_index (i);
539
540    if (feature_tag == g.get_feature_tag (f_index)) {
541      if (feature_index) *feature_index = f_index;
542      return TRUE;
543    }
544  }
545
546  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
547  return FALSE;
548}
549
550unsigned int
551hb_ot_layout_feature_get_lookup_count (hb_ot_layout_t            *layout,
552				       hb_ot_layout_table_type_t  table_type,
553				       unsigned int               feature_index)
554{
555  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
556  const Feature &f = g.get_feature (feature_index);
557
558  return f.get_lookup_count ();
559}
560
561unsigned int
562hb_ot_layout_feature_get_lookup_index (hb_ot_layout_t            *layout,
563				       hb_ot_layout_table_type_t  table_type,
564				       unsigned int               feature_index,
565				       unsigned int               num_lookup)
566{
567  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
568  const Feature &f = g.get_feature (feature_index);
569
570  return f.get_lookup_index (num_lookup);
571}
572
573/*
574 * GSUB
575 */
576
577hb_bool_t
578hb_ot_layout_substitute_lookup (hb_ot_layout_t              *layout,
579				hb_buffer_t                 *buffer,
580			        unsigned int                 lookup_index,
581				hb_ot_layout_feature_mask_t  mask)
582{
583  return layout->gsub->substitute_lookup (layout, buffer, lookup_index, mask);
584}
585
586/*
587 * GPOS
588 */
589
590hb_bool_t
591hb_ot_layout_position_lookup   (hb_ot_layout_t              *layout,
592				hb_buffer_t                 *buffer,
593			        unsigned int                 lookup_index,
594				hb_ot_layout_feature_mask_t  mask)
595{
596  return layout->gpos->position_lookup (layout, buffer, lookup_index, mask);
597}
598