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