hb-ot-layout.cc revision b5db4f1e4eefa266a71a28b5496f47ff9d1a81e8
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
42void
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
59void
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
75static 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
81static 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
87static 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
100hb_bool_t
101hb_ot_layout_has_glyph_classes (hb_face_t *face)
102{
103  return _get_gdef (face).has_glyph_classes ();
104}
105
106hb_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
112static 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
136hb_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
175hb_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
208void
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
260void
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
267hb_ot_layout_glyph_class_t
268hb_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
274void
275hb_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
282void
283hb_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
305unsigned int
306hb_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
315unsigned int
316hb_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
333static const GSUBGPOS&
334get_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
345unsigned int
346hb_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
357hb_bool_t
358hb_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
381hb_bool_t
382hb_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
409unsigned int
410hb_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
422unsigned int
423hb_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
435hb_bool_t
436hb_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
456hb_bool_t
457hb_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
470unsigned int
471hb_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
485unsigned int
486hb_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
510hb_bool_t
511hb_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
536unsigned int
537hb_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
555hb_bool_t
556hb_ot_layout_has_substitution (hb_face_t *face)
557{
558  return &_get_gsub (face) != &Null(GSUB);
559}
560
561hb_bool_t
562hb_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
578hb_bool_t
579hb_ot_layout_has_positioning (hb_face_t *face)
580{
581  return &_get_gpos (face) != &Null(GPOS);
582}
583
584hb_bool_t
585hb_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
597void
598hb_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}
646