hb-ot-layout.cc revision 5130c35e93528bcf3095fee5baf3847589167a58
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)
165{
166  hb_ot_layout_glyph_class_t basic_glyph_class;
167  unsigned int desired_attachment_class;
168
169  if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
170  {
171    ginfo->gproperty = *property = _hb_ot_layout_get_glyph_property (layout, ginfo->gindex);
172    if (ginfo->gproperty == HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED)
173      return false;
174  }
175
176  *property = ginfo->gproperty;
177
178  /* If the glyph was found in the MarkAttachmentClass table,
179   * then that class value is stored in the high byte of the result.
180   * The low byte contains the basic type of the glyph as defined by
181   * the GlyphClassDef table.
182   */
183  if (*property & LookupFlag::MarkAttachmentType)
184    basic_glyph_class = HB_OT_LAYOUT_GLYPH_CLASS_MARK;
185  else
186    basic_glyph_class = (hb_ot_layout_glyph_class_t) *property;
187
188  /* Not covered, if, for example, basic_glyph_class
189   * is HB_GDEF_LIGATURE and lookup_flags includes LookupFlags::IgnoreLigatures
190   */
191  if (lookup_flags & basic_glyph_class)
192    return false;
193
194  /* The high byte of lookup_flags has the meaning
195   * "ignore marks of attachment type different than
196   * the attachment type specified."
197   */
198  desired_attachment_class = lookup_flags & LookupFlag::MarkAttachmentType;
199  if (desired_attachment_class)
200  {
201    if (basic_glyph_class == HB_OT_LAYOUT_GLYPH_CLASS_MARK &&
202        *property != desired_attachment_class)
203      return false;
204  }
205
206  return true;
207}
208
209HB_INTERNAL void
210_hb_ot_layout_set_glyph_property (hb_ot_layout_t *layout,
211				  hb_codepoint_t  glyph,
212				  unsigned int    property)
213{
214  hb_ot_layout_glyph_class_t klass;
215
216  if (property & LookupFlag::MarkAttachmentType)
217    klass = HB_OT_LAYOUT_GLYPH_CLASS_MARK;
218  else
219    klass = (hb_ot_layout_glyph_class_t) property;
220
221  hb_ot_layout_set_glyph_class (layout, glyph, klass);
222}
223
224
225hb_ot_layout_glyph_class_t
226hb_ot_layout_get_glyph_class (hb_ot_layout_t *layout,
227			      hb_codepoint_t  glyph)
228{
229  unsigned int property;
230  hb_ot_layout_class_t klass;
231
232  property = _hb_ot_layout_get_glyph_property (layout, glyph);
233
234  if (property & LookupFlag::MarkAttachmentType)
235    return HB_OT_LAYOUT_GLYPH_CLASS_MARK;
236
237  return (hb_ot_layout_glyph_class_t) property;
238}
239
240void
241hb_ot_layout_set_glyph_class (hb_ot_layout_t             *layout,
242			      hb_codepoint_t              glyph,
243			      hb_ot_layout_glyph_class_t  klass)
244{
245  /* TODO optimize this, similar to old harfbuzz code for example */
246
247  hb_ot_layout_class_t gdef_klass;
248  int len = layout->new_gdef.len;
249
250  if (HB_UNLIKELY (glyph > 65535))
251    return;
252
253  if (glyph >= len) {
254    int new_len;
255    unsigned char *new_klasses;
256
257    new_len = len == 0 ? 120 : 2 * len;
258    if (new_len > 65535)
259      new_len = 65535;
260    new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
261
262    if (HB_UNLIKELY (!new_klasses))
263      return;
264
265    memset (new_klasses + len, 0, new_len - len);
266
267    layout->new_gdef.klasses = new_klasses;
268    layout->new_gdef.len = new_len;
269  }
270
271  switch (klass) {
272  default:
273  case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED:	gdef_klass = GDEF::UnclassifiedGlyph;	break;
274  case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH:	gdef_klass = GDEF::BaseGlyph;		break;
275  case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE:	gdef_klass = GDEF::LigatureGlyph;	break;
276  case HB_OT_LAYOUT_GLYPH_CLASS_MARK:		gdef_klass = GDEF::MarkGlyph;		break;
277  case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT:	gdef_klass = GDEF::ComponentGlyph;	break;
278  }
279
280  layout->new_gdef.klasses[glyph] = gdef_klass;
281  return;
282}
283
284void
285hb_ot_layout_build_glyph_classes (hb_ot_layout_t *layout,
286				  uint16_t        num_total_glyphs,
287				  hb_codepoint_t *glyphs,
288				  unsigned char  *klasses,
289				  uint16_t        count)
290{
291  if (HB_UNLIKELY (!count || !glyphs || !klasses))
292    return;
293
294  if (layout->new_gdef.len == 0) {
295    layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
296    layout->new_gdef.len = count;
297  }
298
299  for (unsigned int i = 0; i < count; i++)
300    hb_ot_layout_set_glyph_class (layout, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
301}
302
303hb_bool_t
304hb_ot_layout_get_attach_points (hb_ot_layout_t *layout,
305				hb_codepoint_t  glyph,
306				unsigned int   *point_count /* IN/OUT */,
307				unsigned int   *point_array /* OUT */)
308{
309  return layout->gdef->get_attach_points (glyph, point_count, point_array);
310}
311
312hb_bool_t
313hb_ot_layout_get_lig_carets (hb_ot_layout_t *layout,
314			     hb_codepoint_t  glyph,
315			     unsigned int   *caret_count /* IN/OUT */,
316			     int            *caret_array /* OUT */)
317{
318  return layout->gdef->get_lig_carets (layout, glyph, caret_count, caret_array);
319}
320
321/*
322 * GSUB/GPOS
323 */
324
325static const GSUBGPOS&
326get_gsubgpos_table (hb_ot_layout_t            *layout,
327		    hb_ot_layout_table_type_t  table_type)
328{
329  switch (table_type) {
330    case HB_OT_LAYOUT_TABLE_TYPE_GSUB: return *(layout->gsub);
331    case HB_OT_LAYOUT_TABLE_TYPE_GPOS: return *(layout->gpos);
332    default:                           return Null(GSUBGPOS);
333  }
334}
335
336
337unsigned int
338hb_ot_layout_table_get_script_count (hb_ot_layout_t            *layout,
339				     hb_ot_layout_table_type_t  table_type)
340{
341  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
342
343  return g.get_script_count ();
344}
345
346hb_tag_t
347hb_ot_layout_table_get_script_tag (hb_ot_layout_t            *layout,
348				   hb_ot_layout_table_type_t  table_type,
349				   unsigned int               script_index)
350{
351  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
352
353  return g.get_script_tag (script_index);
354}
355
356hb_bool_t
357hb_ot_layout_table_find_script (hb_ot_layout_t            *layout,
358				hb_ot_layout_table_type_t  table_type,
359				hb_tag_t                   script_tag,
360				unsigned int              *script_index)
361{
362  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
363  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
364
365  if (g.find_script_index (script_tag, script_index))
366    return TRUE;
367
368  /* try finding 'DFLT' */
369  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
370    return FALSE;
371
372  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
373  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
374    return FALSE;
375
376  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
377  return FALSE;
378}
379
380unsigned int
381hb_ot_layout_table_get_feature_count (hb_ot_layout_t            *layout,
382				      hb_ot_layout_table_type_t  table_type)
383{
384  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
385
386  return g.get_feature_count ();
387}
388
389hb_tag_t
390hb_ot_layout_table_get_feature_tag (hb_ot_layout_t            *layout,
391				    hb_ot_layout_table_type_t  table_type,
392				    unsigned int               feature_index)
393{
394  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
395
396  return g.get_feature_tag (feature_index);
397}
398
399hb_bool_t
400hb_ot_layout_table_find_feature (hb_ot_layout_t            *layout,
401				 hb_ot_layout_table_type_t  table_type,
402				 hb_tag_t                   feature_tag,
403				 unsigned int              *feature_index)
404{
405  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
406  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
407
408  if (g.find_feature_index (feature_tag, feature_index))
409    return TRUE;
410
411  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
412  return FALSE;
413}
414
415unsigned int
416hb_ot_layout_table_get_lookup_count (hb_ot_layout_t            *layout,
417				     hb_ot_layout_table_type_t  table_type)
418{
419  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
420
421  return g.get_lookup_count ();
422}
423
424
425unsigned int
426hb_ot_layout_script_get_language_count (hb_ot_layout_t            *layout,
427					hb_ot_layout_table_type_t  table_type,
428					unsigned int               script_index)
429{
430  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
431
432  return s.get_lang_sys_count ();
433}
434
435hb_tag_t
436hb_ot_layout_script_get_language_tag (hb_ot_layout_t            *layout,
437				      hb_ot_layout_table_type_t  table_type,
438				      unsigned int               script_index,
439				      unsigned int               language_index)
440{
441  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
442
443  return s.get_lang_sys_tag (language_index);
444}
445
446hb_bool_t
447hb_ot_layout_script_find_language (hb_ot_layout_t            *layout,
448				   hb_ot_layout_table_type_t  table_type,
449				   unsigned int               script_index,
450				   hb_tag_t                   language_tag,
451				   unsigned int              *language_index)
452{
453  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
454  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
455
456  if (s.find_lang_sys_index (language_tag, language_index))
457    return TRUE;
458
459  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
460  if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
461    return FALSE;
462
463  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
464  return FALSE;
465}
466
467hb_bool_t
468hb_ot_layout_language_get_required_feature_index (hb_ot_layout_t            *layout,
469						  hb_ot_layout_table_type_t  table_type,
470						  unsigned int               script_index,
471						  unsigned int               language_index,
472						  unsigned int              *feature_index)
473{
474  const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
475
476  if (feature_index) *feature_index = l.get_required_feature_index ();
477
478  return l.has_required_feature ();
479}
480
481unsigned int
482hb_ot_layout_language_get_feature_count (hb_ot_layout_t            *layout,
483					 hb_ot_layout_table_type_t  table_type,
484					 unsigned int               script_index,
485					 unsigned int               language_index)
486{
487  const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
488
489  return l.get_feature_count ();
490}
491
492unsigned int
493hb_ot_layout_language_get_feature_index (hb_ot_layout_t            *layout,
494					 hb_ot_layout_table_type_t  table_type,
495					 unsigned int               script_index,
496					 unsigned int               language_index,
497					 unsigned int               num_feature)
498{
499  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
500  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
501
502  return l.get_feature_index (num_feature);
503}
504
505hb_tag_t
506hb_ot_layout_language_get_feature_tag (hb_ot_layout_t            *layout,
507				       hb_ot_layout_table_type_t  table_type,
508				       unsigned int               script_index,
509				       unsigned int               language_index,
510				       unsigned int               num_feature)
511{
512  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
513  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
514  unsigned int feature_index = l.get_feature_index (num_feature);
515
516  return g.get_feature_tag (feature_index);
517}
518
519
520hb_bool_t
521hb_ot_layout_language_find_feature (hb_ot_layout_t            *layout,
522				    hb_ot_layout_table_type_t  table_type,
523				    unsigned int               script_index,
524				    unsigned int               language_index,
525				    hb_tag_t                   feature_tag,
526				    unsigned int              *feature_index)
527{
528  ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
529  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
530  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
531
532  unsigned int num_features = l.get_feature_count ();
533  for (unsigned int i = 0; i < num_features; i++) {
534    unsigned int f_index = l.get_feature_index (i);
535
536    if (feature_tag == g.get_feature_tag (f_index)) {
537      if (feature_index) *feature_index = f_index;
538      return TRUE;
539    }
540  }
541
542  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
543  return FALSE;
544}
545
546unsigned int
547hb_ot_layout_feature_get_lookup_count (hb_ot_layout_t            *layout,
548				       hb_ot_layout_table_type_t  table_type,
549				       unsigned int               feature_index)
550{
551  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
552  const Feature &f = g.get_feature (feature_index);
553
554  return f.get_lookup_count ();
555}
556
557unsigned int
558hb_ot_layout_feature_get_lookup_index (hb_ot_layout_t            *layout,
559				       hb_ot_layout_table_type_t  table_type,
560				       unsigned int               feature_index,
561				       unsigned int               num_lookup)
562{
563  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
564  const Feature &f = g.get_feature (feature_index);
565
566  return f.get_lookup_index (num_lookup);
567}
568
569/*
570 * GSUB
571 */
572
573hb_bool_t
574hb_ot_layout_substitute_lookup (hb_ot_layout_t              *layout,
575				hb_buffer_t                 *buffer,
576			        unsigned int                 lookup_index,
577				hb_ot_layout_feature_mask_t  mask)
578{
579  return layout->gsub->substitute_lookup (layout, buffer, lookup_index, mask);
580}
581
582/*
583 * GPOS
584 */
585
586hb_bool_t
587hb_ot_layout_position_lookup   (hb_ot_layout_t              *layout,
588				hb_buffer_t                 *buffer,
589			        unsigned int                 lookup_index,
590				hb_ot_layout_feature_mask_t  mask)
591{
592  return layout->gpos->position_lookup (layout, buffer, lookup_index, mask);
593}
594