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