Prefer C linkage
[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,2009  Red Hat, Inc.
5  *
6  *  This is part of HarfBuzz, a text shaping 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.hh"
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 HB_BEGIN_DECLS
42
43
44 hb_ot_layout_t *
45 _hb_ot_layout_new (hb_face_t *face)
46 {
47   /* Remove this object altogether */
48   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
49
50   layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_get_table (face, HB_OT_TAG_GDEF));
51   layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
52
53   layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_get_table (face, HB_OT_TAG_GSUB));
54   layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
55
56   layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_get_table (face, HB_OT_TAG_GPOS));
57   layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
58
59   return layout;
60 }
61
62 void
63 _hb_ot_layout_free (hb_ot_layout_t *layout)
64 {
65   hb_blob_unlock (layout->gdef_blob);
66   hb_blob_unlock (layout->gsub_blob);
67   hb_blob_unlock (layout->gpos_blob);
68
69   hb_blob_destroy (layout->gdef_blob);
70   hb_blob_destroy (layout->gsub_blob);
71   hb_blob_destroy (layout->gpos_blob);
72
73   free (layout->new_gdef.klasses);
74 }
75
76 static const GDEF&
77 _get_gdef (hb_face_t *face)
78 {
79   return likely (face->ot_layout->gdef) ? *face->ot_layout->gdef : Null(GDEF);
80 }
81
82 static const GSUB&
83 _get_gsub (hb_face_t *face)
84 {
85   return likely (face->ot_layout->gsub) ? *face->ot_layout->gsub : Null(GSUB);
86 }
87
88 static const GPOS&
89 _get_gpos (hb_face_t *face)
90 {
91   return likely (face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS);
92 }
93
94
95 /*
96  * GDEF
97  */
98
99 /* TODO the public class_t is a mess */
100
101 hb_bool_t
102 hb_ot_layout_has_glyph_classes (hb_face_t *face)
103 {
104   return _get_gdef (face).has_glyph_classes ();
105 }
106
107 hb_bool_t
108 _hb_ot_layout_has_new_glyph_classes (hb_face_t *face)
109 {
110   return face->ot_layout->new_gdef.len > 0;
111 }
112
113 static unsigned int
114 _hb_ot_layout_get_glyph_property (hb_face_t      *face,
115                                   hb_codepoint_t  glyph)
116 {
117   hb_ot_layout_class_t klass;
118   const GDEF &gdef = _get_gdef (face);
119
120   klass = gdef.get_glyph_class (glyph);
121
122   if (!klass && glyph < face->ot_layout->new_gdef.len)
123     klass = face->ot_layout->new_gdef.klasses[glyph];
124
125   switch (klass) {
126   default:
127   case GDEF::UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
128   case GDEF::BaseGlyph:         return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
129   case GDEF::LigatureGlyph:     return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
130   case GDEF::ComponentGlyph:    return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
131   case GDEF::MarkGlyph:
132         klass = gdef.get_mark_attachment_type (glyph);
133         return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8);
134   }
135 }
136
137 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 & LookupFlag::IgnoreFlags)
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
176 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
209 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   unsigned int len = layout->new_gdef.len;
222
223   if (unlikely (glyph > 65535))
224     return;
225
226   /* XXX this is not threadsafe */
227   if (glyph >= len) {
228     unsigned int new_len;
229     unsigned char *new_klasses;
230
231     new_len = len == 0 ? 120 : 2 * len;
232     while (new_len <= glyph)
233       new_len *= 2;
234
235     if (new_len > 65536)
236       new_len = 65536;
237     new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
238
239     if (unlikely (!new_klasses))
240       return;
241
242     memset (new_klasses + len, 0, new_len - len);
243
244     layout->new_gdef.klasses = new_klasses;
245     layout->new_gdef.len = new_len;
246   }
247
248   switch (klass) {
249   default:
250   case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED:   gdef_klass = GDEF::UnclassifiedGlyph;   break;
251   case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH:     gdef_klass = GDEF::BaseGlyph;           break;
252   case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE:       gdef_klass = GDEF::LigatureGlyph;       break;
253   case HB_OT_LAYOUT_GLYPH_CLASS_MARK:           gdef_klass = GDEF::MarkGlyph;           break;
254   case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT:      gdef_klass = GDEF::ComponentGlyph;      break;
255   }
256
257   layout->new_gdef.klasses[glyph] = gdef_klass;
258   return;
259 }
260
261 void
262 _hb_ot_layout_set_glyph_property (hb_face_t      *face,
263                                   hb_codepoint_t  glyph,
264                                   unsigned int    property)
265 { _hb_ot_layout_set_glyph_class (face, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); }
266
267
268 hb_ot_layout_glyph_class_t
269 hb_ot_layout_get_glyph_class (hb_face_t      *face,
270                               hb_codepoint_t  glyph)
271 {
272   return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (face, glyph) & 0xff);
273 }
274
275 void
276 hb_ot_layout_set_glyph_class (hb_face_t                 *face,
277                               hb_codepoint_t             glyph,
278                               hb_ot_layout_glyph_class_t klass)
279 {
280   _hb_ot_layout_set_glyph_class (face, glyph, klass);
281 }
282
283 void
284 hb_ot_layout_build_glyph_classes (hb_face_t      *face,
285                                   hb_codepoint_t *glyphs,
286                                   unsigned char  *klasses,
287                                   uint16_t        count)
288 {
289   if (HB_OBJECT_IS_INERT (face))
290     return;
291
292   hb_ot_layout_t *layout = face->ot_layout;
293
294   if (unlikely (!count || !glyphs || !klasses))
295     return;
296
297   if (layout->new_gdef.len == 0) {
298     layout->new_gdef.klasses = (unsigned char *) calloc (count, sizeof (unsigned char));
299     layout->new_gdef.len = count;
300   }
301
302   for (unsigned int i = 0; i < count; i++)
303     _hb_ot_layout_set_glyph_class (face, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
304 }
305
306 unsigned int
307 hb_ot_layout_get_attach_points (hb_face_t      *face,
308                                 hb_codepoint_t  glyph,
309                                 unsigned int    start_offset,
310                                 unsigned int   *point_count /* IN/OUT */,
311                                 unsigned int   *point_array /* OUT */)
312 {
313   return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
314 }
315
316 unsigned int
317 hb_ot_layout_get_lig_carets (hb_font_t      *font,
318                              hb_face_t      *face,
319                              hb_codepoint_t  glyph,
320                              unsigned int    start_offset,
321                              unsigned int   *caret_count /* IN/OUT */,
322                              int            *caret_array /* OUT */)
323 {
324   hb_ot_layout_context_t c;
325   c.font = font;
326   c.face = face;
327   return _get_gdef (face).get_lig_carets (&c, glyph, start_offset, caret_count, caret_array);
328 }
329
330 /*
331  * GSUB/GPOS
332  */
333
334 static const GSUBGPOS&
335 get_gsubgpos_table (hb_face_t *face,
336                     hb_tag_t   table_tag)
337 {
338   switch (table_tag) {
339     case HB_OT_TAG_GSUB: return _get_gsub (face);
340     case HB_OT_TAG_GPOS: return _get_gpos (face);
341     default:             return Null(GSUBGPOS);
342   }
343 }
344
345
346 unsigned int
347 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
348                                     hb_tag_t      table_tag,
349                                     unsigned int  start_offset,
350                                     unsigned int *script_count /* IN/OUT */,
351                                     hb_tag_t     *script_tags /* OUT */)
352 {
353   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
354
355   return g.get_script_tags (start_offset, script_count, script_tags);
356 }
357
358 hb_bool_t
359 hb_ot_layout_table_find_script (hb_face_t    *face,
360                                 hb_tag_t      table_tag,
361                                 hb_tag_t      script_tag,
362                                 unsigned int *script_index)
363 {
364   ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
365   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
366
367   if (g.find_script_index (script_tag, script_index))
368     return TRUE;
369
370   /* try finding 'DFLT' */
371   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
372     return FALSE;
373
374   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
375   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
376     return FALSE;
377
378   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
379   return FALSE;
380 }
381
382 hb_bool_t
383 hb_ot_layout_table_choose_script (hb_face_t      *face,
384                                   hb_tag_t        table_tag,
385                                   const hb_tag_t *script_tags,
386                                   unsigned int   *script_index)
387 {
388   ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
389   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
390
391   while (*script_tags)
392   {
393     if (g.find_script_index (*script_tags, script_index))
394       return TRUE;
395     script_tags++;
396   }
397
398   /* try finding 'DFLT' */
399   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
400     return FALSE;
401
402   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
403   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
404     return FALSE;
405
406   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
407   return FALSE;
408 }
409
410 unsigned int
411 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
412                                      hb_tag_t      table_tag,
413                                      unsigned int  start_offset,
414                                      unsigned int *feature_count /* IN/OUT */,
415                                      hb_tag_t     *feature_tags /* OUT */)
416 {
417   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
418
419   return g.get_feature_tags (start_offset, feature_count, feature_tags);
420 }
421
422
423 unsigned int
424 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
425                                        hb_tag_t      table_tag,
426                                        unsigned int  script_index,
427                                        unsigned int  start_offset,
428                                        unsigned int *language_count /* IN/OUT */,
429                                        hb_tag_t     *language_tags /* OUT */)
430 {
431   const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
432
433   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
434 }
435
436 hb_bool_t
437 hb_ot_layout_script_find_language (hb_face_t    *face,
438                                    hb_tag_t      table_tag,
439                                    unsigned int  script_index,
440                                    hb_tag_t      language_tag,
441                                    unsigned int *language_index)
442 {
443   ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
444   const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
445
446   if (s.find_lang_sys_index (language_tag, language_index))
447     return TRUE;
448
449   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
450   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
451     return FALSE;
452
453   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
454   return FALSE;
455 }
456
457 hb_bool_t
458 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
459                                                   hb_tag_t      table_tag,
460                                                   unsigned int  script_index,
461                                                   unsigned int  language_index,
462                                                   unsigned int *feature_index)
463 {
464   const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
465
466   if (feature_index) *feature_index = l.get_required_feature_index ();
467
468   return l.has_required_feature ();
469 }
470
471 unsigned int
472 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
473                                            hb_tag_t      table_tag,
474                                            unsigned int  script_index,
475                                            unsigned int  language_index,
476                                            unsigned int  start_offset,
477                                            unsigned int *feature_count /* IN/OUT */,
478                                            unsigned int *feature_indexes /* OUT */)
479 {
480   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
481   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
482
483   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
484 }
485
486 unsigned int
487 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
488                                         hb_tag_t      table_tag,
489                                         unsigned int  script_index,
490                                         unsigned int  language_index,
491                                         unsigned int  start_offset,
492                                         unsigned int *feature_count /* IN/OUT */,
493                                         hb_tag_t     *feature_tags /* OUT */)
494 {
495   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
496   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
497
498   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
499   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
500
501   if (feature_tags) {
502     unsigned int count = *feature_count;
503     for (unsigned int i = 0; i < count; i++)
504       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
505   }
506
507   return ret;
508 }
509
510
511 hb_bool_t
512 hb_ot_layout_language_find_feature (hb_face_t    *face,
513                                     hb_tag_t      table_tag,
514                                     unsigned int  script_index,
515                                     unsigned int  language_index,
516                                     hb_tag_t      feature_tag,
517                                     unsigned int *feature_index)
518 {
519   ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
520   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
521   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
522
523   unsigned int num_features = l.get_feature_count ();
524   for (unsigned int i = 0; i < num_features; i++) {
525     unsigned int f_index = l.get_feature_index (i);
526
527     if (feature_tag == g.get_feature_tag (f_index)) {
528       if (feature_index) *feature_index = f_index;
529       return TRUE;
530     }
531   }
532
533   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
534   return FALSE;
535 }
536
537 unsigned int
538 hb_ot_layout_feature_get_lookup_indexes (hb_face_t    *face,
539                                          hb_tag_t      table_tag,
540                                          unsigned int  feature_index,
541                                          unsigned int  start_offset,
542                                          unsigned int *lookup_count /* IN/OUT */,
543                                          unsigned int *lookup_indexes /* OUT */)
544 {
545   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
546   const Feature &f = g.get_feature (feature_index);
547
548   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
549 }
550
551
552 /*
553  * GSUB
554  */
555
556 hb_bool_t
557 hb_ot_layout_has_substitution (hb_face_t *face)
558 {
559   return &_get_gsub (face) != &Null(GSUB);
560 }
561
562 hb_bool_t
563 hb_ot_layout_substitute_lookup (hb_face_t    *face,
564                                 hb_buffer_t  *buffer,
565                                 unsigned int  lookup_index,
566                                 hb_mask_t     mask)
567 {
568   hb_ot_layout_context_t c;
569   c.font = NULL;
570   c.face = face;
571   return _get_gsub (face).substitute_lookup (&c, buffer, lookup_index, mask);
572 }
573
574
575 /*
576  * GPOS
577  */
578
579 hb_bool_t
580 hb_ot_layout_has_positioning (hb_face_t *face)
581 {
582   return &_get_gpos (face) != &Null(GPOS);
583 }
584
585 hb_bool_t
586 hb_ot_layout_position_lookup   (hb_font_t    *font,
587                                 hb_face_t    *face,
588                                 hb_buffer_t  *buffer,
589                                 unsigned int  lookup_index,
590                                 hb_mask_t     mask)
591 {
592   hb_ot_layout_context_t c;
593   c.font = font;
594   c.face = face;
595   return _get_gpos (face).position_lookup (&c, buffer, lookup_index, mask);
596 }
597
598 void
599 hb_ot_layout_position_finish (hb_font_t    *font HB_UNUSED,
600                               hb_face_t    *face HB_UNUSED,
601                               hb_buffer_t  *buffer)
602 {
603   unsigned int i, j;
604   unsigned int len = hb_buffer_get_length (buffer);
605   hb_internal_glyph_position_t *pos = (hb_internal_glyph_position_t *) hb_buffer_get_glyph_positions (buffer);
606
607   /* TODO: Vertical */
608
609   /* Handle cursive connections */
610   /* First handle all left-to-right connections */
611   for (j = 0; j < len; j++) {
612     if (pos[j].cursive_chain > 0)
613     {
614       pos[j].y_offset += pos[j - pos[j].cursive_chain].y_offset;
615       pos[j].cursive_chain = 0;
616     }
617   }
618   /* Then handle all right-to-left connections */
619   for (i = len; i > 0; i--) {
620     j = i - 1;
621     if (pos[j].cursive_chain < 0)
622     {
623       pos[j].y_offset += pos[j - pos[j].cursive_chain].y_offset;
624       pos[j].cursive_chain = 0;
625     }
626   }
627
628   /* Handle attachments */
629   for (i = 0; i < len; i++)
630     if (pos[i].back)
631     {
632       unsigned int back = i - pos[i].back;
633       pos[i].back = 0;
634       pos[i].x_offset += pos[back].x_offset;
635       pos[i].y_offset += pos[back].y_offset;
636
637       if (buffer->direction == HB_DIRECTION_RTL)
638         for (j = back + 1; j < i + 1; j++) {
639           pos[i].x_offset += pos[j].x_advance;
640           pos[i].y_offset += pos[j].y_advance;
641         }
642       else
643         for (j = back; j < i; j++) {
644           pos[i].x_offset -= pos[j].x_advance;
645           pos[i].y_offset -= pos[j].y_advance;
646         }
647     }
648 }
649
650
651 HB_END_DECLS