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