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