1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright © 2011  Martin Hosken
3cb93a386Sopenharmony_ci * Copyright © 2011  SIL International
4cb93a386Sopenharmony_ci * Copyright © 2011,2012  Google, Inc.
5cb93a386Sopenharmony_ci *
6cb93a386Sopenharmony_ci *  This is part of HarfBuzz, a text shaping library.
7cb93a386Sopenharmony_ci *
8cb93a386Sopenharmony_ci * Permission is hereby granted, without written agreement and without
9cb93a386Sopenharmony_ci * license or royalty fees, to use, copy, modify, and distribute this
10cb93a386Sopenharmony_ci * software and its documentation for any purpose, provided that the
11cb93a386Sopenharmony_ci * above copyright notice and the following two paragraphs appear in
12cb93a386Sopenharmony_ci * all copies of this software.
13cb93a386Sopenharmony_ci *
14cb93a386Sopenharmony_ci * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15cb93a386Sopenharmony_ci * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16cb93a386Sopenharmony_ci * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17cb93a386Sopenharmony_ci * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18cb93a386Sopenharmony_ci * DAMAGE.
19cb93a386Sopenharmony_ci *
20cb93a386Sopenharmony_ci * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21cb93a386Sopenharmony_ci * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22cb93a386Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23cb93a386Sopenharmony_ci * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24cb93a386Sopenharmony_ci * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25cb93a386Sopenharmony_ci *
26cb93a386Sopenharmony_ci * Google Author(s): Behdad Esfahbod
27cb93a386Sopenharmony_ci */
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci#include "hb.hh"
30cb93a386Sopenharmony_ci
31cb93a386Sopenharmony_ci#ifdef HAVE_GRAPHITE2
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci#include "hb-shaper-impl.hh"
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ci#include "hb-graphite2.h"
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_ci#include <graphite2/Segment.h>
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci#include "hb-ot-layout.h"
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci/**
43cb93a386Sopenharmony_ci * SECTION:hb-graphite2
44cb93a386Sopenharmony_ci * @title: hb-graphite2
45cb93a386Sopenharmony_ci * @short_description: Graphite2 integration
46cb93a386Sopenharmony_ci * @include: hb-graphite2.h
47cb93a386Sopenharmony_ci *
48cb93a386Sopenharmony_ci * Functions for using HarfBuzz with fonts that include Graphite features.
49cb93a386Sopenharmony_ci *
50cb93a386Sopenharmony_ci * For Graphite features to work, you must be sure that HarfBuzz was compiled
51cb93a386Sopenharmony_ci * with the `graphite2` shaping engine enabled. Currently, the default is to
52cb93a386Sopenharmony_ci * not enable `graphite2` shaping.
53cb93a386Sopenharmony_ci **/
54cb93a386Sopenharmony_ci
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci/*
57cb93a386Sopenharmony_ci * shaper face data
58cb93a386Sopenharmony_ci */
59cb93a386Sopenharmony_ci
60cb93a386Sopenharmony_citypedef struct hb_graphite2_tablelist_t
61cb93a386Sopenharmony_ci{
62cb93a386Sopenharmony_ci  struct hb_graphite2_tablelist_t *next;
63cb93a386Sopenharmony_ci  hb_blob_t *blob;
64cb93a386Sopenharmony_ci  unsigned int tag;
65cb93a386Sopenharmony_ci} hb_graphite2_tablelist_t;
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_cistruct hb_graphite2_face_data_t
68cb93a386Sopenharmony_ci{
69cb93a386Sopenharmony_ci  hb_face_t *face;
70cb93a386Sopenharmony_ci  gr_face   *grface;
71cb93a386Sopenharmony_ci  hb_atomic_ptr_t<hb_graphite2_tablelist_t> tlist;
72cb93a386Sopenharmony_ci};
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_cistatic const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len)
75cb93a386Sopenharmony_ci{
76cb93a386Sopenharmony_ci  hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data;
77cb93a386Sopenharmony_ci  hb_graphite2_tablelist_t *tlist = face_data->tlist;
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci  hb_blob_t *blob = nullptr;
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_ci  for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next)
82cb93a386Sopenharmony_ci    if (p->tag == tag) {
83cb93a386Sopenharmony_ci      blob = p->blob;
84cb93a386Sopenharmony_ci      break;
85cb93a386Sopenharmony_ci    }
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci  if (unlikely (!blob))
88cb93a386Sopenharmony_ci  {
89cb93a386Sopenharmony_ci    blob = face_data->face->reference_table (tag);
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_ci    hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) hb_calloc (1, sizeof (hb_graphite2_tablelist_t));
92cb93a386Sopenharmony_ci    if (unlikely (!p)) {
93cb93a386Sopenharmony_ci      hb_blob_destroy (blob);
94cb93a386Sopenharmony_ci      return nullptr;
95cb93a386Sopenharmony_ci    }
96cb93a386Sopenharmony_ci    p->blob = blob;
97cb93a386Sopenharmony_ci    p->tag = tag;
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ciretry:
100cb93a386Sopenharmony_ci    hb_graphite2_tablelist_t *tlist = face_data->tlist;
101cb93a386Sopenharmony_ci    p->next = tlist;
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci    if (unlikely (!face_data->tlist.cmpexch (tlist, p)))
104cb93a386Sopenharmony_ci      goto retry;
105cb93a386Sopenharmony_ci  }
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci  unsigned int tlen;
108cb93a386Sopenharmony_ci  const char *d = hb_blob_get_data (blob, &tlen);
109cb93a386Sopenharmony_ci  *len = tlen;
110cb93a386Sopenharmony_ci  return d;
111cb93a386Sopenharmony_ci}
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_cihb_graphite2_face_data_t *
114cb93a386Sopenharmony_ci_hb_graphite2_shaper_face_data_create (hb_face_t *face)
115cb93a386Sopenharmony_ci{
116cb93a386Sopenharmony_ci  hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF);
117cb93a386Sopenharmony_ci  /* Umm, we just reference the table to check whether it exists.
118cb93a386Sopenharmony_ci   * Maybe add better API for this? */
119cb93a386Sopenharmony_ci  if (!hb_blob_get_length (silf_blob))
120cb93a386Sopenharmony_ci  {
121cb93a386Sopenharmony_ci    hb_blob_destroy (silf_blob);
122cb93a386Sopenharmony_ci    return nullptr;
123cb93a386Sopenharmony_ci  }
124cb93a386Sopenharmony_ci  hb_blob_destroy (silf_blob);
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci  hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) hb_calloc (1, sizeof (hb_graphite2_face_data_t));
127cb93a386Sopenharmony_ci  if (unlikely (!data))
128cb93a386Sopenharmony_ci    return nullptr;
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci  data->face = face;
131cb93a386Sopenharmony_ci  const gr_face_ops ops = {sizeof(gr_face_ops), &hb_graphite2_get_table, NULL};
132cb93a386Sopenharmony_ci  data->grface = gr_make_face_with_ops (data, &ops, gr_face_preloadAll);
133cb93a386Sopenharmony_ci
134cb93a386Sopenharmony_ci  if (unlikely (!data->grface)) {
135cb93a386Sopenharmony_ci    hb_free (data);
136cb93a386Sopenharmony_ci    return nullptr;
137cb93a386Sopenharmony_ci  }
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci  return data;
140cb93a386Sopenharmony_ci}
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_civoid
143cb93a386Sopenharmony_ci_hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data)
144cb93a386Sopenharmony_ci{
145cb93a386Sopenharmony_ci  hb_graphite2_tablelist_t *tlist = data->tlist;
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_ci  while (tlist)
148cb93a386Sopenharmony_ci  {
149cb93a386Sopenharmony_ci    hb_graphite2_tablelist_t *old = tlist;
150cb93a386Sopenharmony_ci    hb_blob_destroy (tlist->blob);
151cb93a386Sopenharmony_ci    tlist = tlist->next;
152cb93a386Sopenharmony_ci    hb_free (old);
153cb93a386Sopenharmony_ci  }
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci  gr_face_destroy (data->grface);
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_ci  hb_free (data);
158cb93a386Sopenharmony_ci}
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci/**
161cb93a386Sopenharmony_ci * hb_graphite2_face_get_gr_face:
162cb93a386Sopenharmony_ci * @face: @hb_face_t to query
163cb93a386Sopenharmony_ci *
164cb93a386Sopenharmony_ci * Fetches the Graphite2 gr_face corresponding to the specified
165cb93a386Sopenharmony_ci * #hb_face_t face object.
166cb93a386Sopenharmony_ci *
167cb93a386Sopenharmony_ci * Return value: the gr_face found
168cb93a386Sopenharmony_ci *
169cb93a386Sopenharmony_ci * Since: 0.9.10
170cb93a386Sopenharmony_ci */
171cb93a386Sopenharmony_cigr_face *
172cb93a386Sopenharmony_cihb_graphite2_face_get_gr_face (hb_face_t *face)
173cb93a386Sopenharmony_ci{
174cb93a386Sopenharmony_ci  const hb_graphite2_face_data_t *data = face->data.graphite2;
175cb93a386Sopenharmony_ci  return data ? data->grface : nullptr;
176cb93a386Sopenharmony_ci}
177cb93a386Sopenharmony_ci
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci/*
180cb93a386Sopenharmony_ci * shaper font data
181cb93a386Sopenharmony_ci */
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_cistruct hb_graphite2_font_data_t {};
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_cihb_graphite2_font_data_t *
186cb93a386Sopenharmony_ci_hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED)
187cb93a386Sopenharmony_ci{
188cb93a386Sopenharmony_ci  return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
189cb93a386Sopenharmony_ci}
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_civoid
192cb93a386Sopenharmony_ci_hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED)
193cb93a386Sopenharmony_ci{
194cb93a386Sopenharmony_ci}
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ci#ifndef HB_DISABLE_DEPRECATED
197cb93a386Sopenharmony_ci/**
198cb93a386Sopenharmony_ci * hb_graphite2_font_get_gr_font:
199cb93a386Sopenharmony_ci * @font: An #hb_font_t
200cb93a386Sopenharmony_ci *
201cb93a386Sopenharmony_ci * Always returns %NULL. Use hb_graphite2_face_get_gr_face() instead.
202cb93a386Sopenharmony_ci *
203cb93a386Sopenharmony_ci * Return value: (nullable): Graphite2 font associated with @font.
204cb93a386Sopenharmony_ci *
205cb93a386Sopenharmony_ci * Since: 0.9.10
206cb93a386Sopenharmony_ci * Deprecated: 1.4.2
207cb93a386Sopenharmony_ci */
208cb93a386Sopenharmony_cigr_font *
209cb93a386Sopenharmony_cihb_graphite2_font_get_gr_font (hb_font_t *font HB_UNUSED)
210cb93a386Sopenharmony_ci{
211cb93a386Sopenharmony_ci  return nullptr;
212cb93a386Sopenharmony_ci}
213cb93a386Sopenharmony_ci#endif
214cb93a386Sopenharmony_ci
215cb93a386Sopenharmony_ci
216cb93a386Sopenharmony_ci/*
217cb93a386Sopenharmony_ci * shaper
218cb93a386Sopenharmony_ci */
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_cistruct hb_graphite2_cluster_t {
221cb93a386Sopenharmony_ci  unsigned int base_char;
222cb93a386Sopenharmony_ci  unsigned int num_chars;
223cb93a386Sopenharmony_ci  unsigned int base_glyph;
224cb93a386Sopenharmony_ci  unsigned int num_glyphs;
225cb93a386Sopenharmony_ci  unsigned int cluster;
226cb93a386Sopenharmony_ci  unsigned int advance;
227cb93a386Sopenharmony_ci};
228cb93a386Sopenharmony_ci
229cb93a386Sopenharmony_cihb_bool_t
230cb93a386Sopenharmony_ci_hb_graphite2_shape (hb_shape_plan_t    *shape_plan HB_UNUSED,
231cb93a386Sopenharmony_ci		     hb_font_t          *font,
232cb93a386Sopenharmony_ci		     hb_buffer_t        *buffer,
233cb93a386Sopenharmony_ci		     const hb_feature_t *features,
234cb93a386Sopenharmony_ci		     unsigned int        num_features)
235cb93a386Sopenharmony_ci{
236cb93a386Sopenharmony_ci  hb_face_t *face = font->face;
237cb93a386Sopenharmony_ci  gr_face *grface = face->data.graphite2->grface;
238cb93a386Sopenharmony_ci
239cb93a386Sopenharmony_ci  const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
240cb93a386Sopenharmony_ci  const char *lang_end = lang ? strchr (lang, '-') : nullptr;
241cb93a386Sopenharmony_ci  int lang_len = lang_end ? lang_end - lang : -1;
242cb93a386Sopenharmony_ci  gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
243cb93a386Sopenharmony_ci
244cb93a386Sopenharmony_ci  for (unsigned int i = 0; i < num_features; i++)
245cb93a386Sopenharmony_ci  {
246cb93a386Sopenharmony_ci    const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag);
247cb93a386Sopenharmony_ci    if (fref)
248cb93a386Sopenharmony_ci      gr_fref_set_feature_value (fref, features[i].value, feats);
249cb93a386Sopenharmony_ci  }
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_ci  gr_segment *seg = nullptr;
252cb93a386Sopenharmony_ci  const gr_slot *is;
253cb93a386Sopenharmony_ci  unsigned int ci = 0, ic = 0;
254cb93a386Sopenharmony_ci  unsigned int curradvx = 0, curradvy = 0;
255cb93a386Sopenharmony_ci
256cb93a386Sopenharmony_ci  unsigned int scratch_size;
257cb93a386Sopenharmony_ci  hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
258cb93a386Sopenharmony_ci
259cb93a386Sopenharmony_ci  uint32_t *chars = (uint32_t *) scratch;
260cb93a386Sopenharmony_ci
261cb93a386Sopenharmony_ci  for (unsigned int i = 0; i < buffer->len; ++i)
262cb93a386Sopenharmony_ci    chars[i] = buffer->info[i].codepoint;
263cb93a386Sopenharmony_ci
264cb93a386Sopenharmony_ci  /* TODO ensure_native_direction. */
265cb93a386Sopenharmony_ci
266cb93a386Sopenharmony_ci  hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT];
267cb93a386Sopenharmony_ci  unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT;
268cb93a386Sopenharmony_ci  hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer),
269cb93a386Sopenharmony_ci				       HB_LANGUAGE_INVALID,
270cb93a386Sopenharmony_ci				       &count,
271cb93a386Sopenharmony_ci				       script_tag,
272cb93a386Sopenharmony_ci				       nullptr, nullptr);
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_ci  seg = gr_make_seg (nullptr, grface,
275cb93a386Sopenharmony_ci		     count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT,
276cb93a386Sopenharmony_ci		     feats,
277cb93a386Sopenharmony_ci		     gr_utf32, chars, buffer->len,
278cb93a386Sopenharmony_ci		     2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_ci  if (unlikely (!seg)) {
281cb93a386Sopenharmony_ci    if (feats) gr_featureval_destroy (feats);
282cb93a386Sopenharmony_ci    return false;
283cb93a386Sopenharmony_ci  }
284cb93a386Sopenharmony_ci
285cb93a386Sopenharmony_ci  unsigned int glyph_count = gr_seg_n_slots (seg);
286cb93a386Sopenharmony_ci  if (unlikely (!glyph_count)) {
287cb93a386Sopenharmony_ci    if (feats) gr_featureval_destroy (feats);
288cb93a386Sopenharmony_ci    gr_seg_destroy (seg);
289cb93a386Sopenharmony_ci    buffer->len = 0;
290cb93a386Sopenharmony_ci    return true;
291cb93a386Sopenharmony_ci  }
292cb93a386Sopenharmony_ci
293cb93a386Sopenharmony_ci  (void) buffer->ensure (glyph_count);
294cb93a386Sopenharmony_ci  scratch = buffer->get_scratch_buffer (&scratch_size);
295cb93a386Sopenharmony_ci  while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) +
296cb93a386Sopenharmony_ci	  DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size)
297cb93a386Sopenharmony_ci  {
298cb93a386Sopenharmony_ci    if (unlikely (!buffer->ensure (buffer->allocated * 2)))
299cb93a386Sopenharmony_ci    {
300cb93a386Sopenharmony_ci      if (feats) gr_featureval_destroy (feats);
301cb93a386Sopenharmony_ci      gr_seg_destroy (seg);
302cb93a386Sopenharmony_ci      return false;
303cb93a386Sopenharmony_ci    }
304cb93a386Sopenharmony_ci    scratch = buffer->get_scratch_buffer (&scratch_size);
305cb93a386Sopenharmony_ci  }
306cb93a386Sopenharmony_ci
307cb93a386Sopenharmony_ci#define ALLOCATE_ARRAY(Type, name, len) \
308cb93a386Sopenharmony_ci  Type *name = (Type *) scratch; \
309cb93a386Sopenharmony_ci  do { \
310cb93a386Sopenharmony_ci    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
311cb93a386Sopenharmony_ci    assert (_consumed <= scratch_size); \
312cb93a386Sopenharmony_ci    scratch += _consumed; \
313cb93a386Sopenharmony_ci    scratch_size -= _consumed; \
314cb93a386Sopenharmony_ci  } while (0)
315cb93a386Sopenharmony_ci
316cb93a386Sopenharmony_ci  ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len);
317cb93a386Sopenharmony_ci  ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count);
318cb93a386Sopenharmony_ci
319cb93a386Sopenharmony_ci#undef ALLOCATE_ARRAY
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_ci  memset (clusters, 0, sizeof (clusters[0]) * buffer->len);
322cb93a386Sopenharmony_ci
323cb93a386Sopenharmony_ci  hb_codepoint_t *pg = gids;
324cb93a386Sopenharmony_ci  clusters[0].cluster = buffer->info[0].cluster;
325cb93a386Sopenharmony_ci  unsigned int upem = hb_face_get_upem (face);
326cb93a386Sopenharmony_ci  float xscale = (float) font->x_scale / upem;
327cb93a386Sopenharmony_ci  float yscale = (float) font->y_scale / upem;
328cb93a386Sopenharmony_ci  yscale *= yscale / xscale;
329cb93a386Sopenharmony_ci  unsigned int curradv = 0;
330cb93a386Sopenharmony_ci  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
331cb93a386Sopenharmony_ci  {
332cb93a386Sopenharmony_ci    curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale;
333cb93a386Sopenharmony_ci    clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv;
334cb93a386Sopenharmony_ci  }
335cb93a386Sopenharmony_ci  else
336cb93a386Sopenharmony_ci    clusters[0].advance = 0;
337cb93a386Sopenharmony_ci  for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
338cb93a386Sopenharmony_ci  {
339cb93a386Sopenharmony_ci    unsigned int before = gr_slot_before (is);
340cb93a386Sopenharmony_ci    unsigned int after = gr_slot_after (is);
341cb93a386Sopenharmony_ci    *pg = gr_slot_gid (is);
342cb93a386Sopenharmony_ci    pg++;
343cb93a386Sopenharmony_ci    while (clusters[ci].base_char > before && ci)
344cb93a386Sopenharmony_ci    {
345cb93a386Sopenharmony_ci      clusters[ci-1].num_chars += clusters[ci].num_chars;
346cb93a386Sopenharmony_ci      clusters[ci-1].num_glyphs += clusters[ci].num_glyphs;
347cb93a386Sopenharmony_ci      clusters[ci-1].advance += clusters[ci].advance;
348cb93a386Sopenharmony_ci      ci--;
349cb93a386Sopenharmony_ci    }
350cb93a386Sopenharmony_ci
351cb93a386Sopenharmony_ci    if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars)
352cb93a386Sopenharmony_ci    {
353cb93a386Sopenharmony_ci      hb_graphite2_cluster_t *c = clusters + ci + 1;
354cb93a386Sopenharmony_ci      c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
355cb93a386Sopenharmony_ci      c->cluster = buffer->info[c->base_char].cluster;
356cb93a386Sopenharmony_ci      c->num_chars = before - c->base_char;
357cb93a386Sopenharmony_ci      c->base_glyph = ic;
358cb93a386Sopenharmony_ci      c->num_glyphs = 0;
359cb93a386Sopenharmony_ci      if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
360cb93a386Sopenharmony_ci      {
361cb93a386Sopenharmony_ci	c->advance = curradv - gr_slot_origin_X(is) * xscale;
362cb93a386Sopenharmony_ci	curradv -= c->advance;
363cb93a386Sopenharmony_ci      }
364cb93a386Sopenharmony_ci      else
365cb93a386Sopenharmony_ci      {
366cb93a386Sopenharmony_ci	c->advance = 0;
367cb93a386Sopenharmony_ci	clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv;
368cb93a386Sopenharmony_ci	curradv += clusters[ci].advance;
369cb93a386Sopenharmony_ci      }
370cb93a386Sopenharmony_ci      ci++;
371cb93a386Sopenharmony_ci    }
372cb93a386Sopenharmony_ci    clusters[ci].num_glyphs++;
373cb93a386Sopenharmony_ci
374cb93a386Sopenharmony_ci    if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
375cb93a386Sopenharmony_ci	clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
376cb93a386Sopenharmony_ci  }
377cb93a386Sopenharmony_ci
378cb93a386Sopenharmony_ci  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
379cb93a386Sopenharmony_ci    clusters[ci].advance += curradv;
380cb93a386Sopenharmony_ci  else
381cb93a386Sopenharmony_ci    clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv;
382cb93a386Sopenharmony_ci  ci++;
383cb93a386Sopenharmony_ci
384cb93a386Sopenharmony_ci  for (unsigned int i = 0; i < ci; ++i)
385cb93a386Sopenharmony_ci  {
386cb93a386Sopenharmony_ci    for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
387cb93a386Sopenharmony_ci    {
388cb93a386Sopenharmony_ci      hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j];
389cb93a386Sopenharmony_ci      info->codepoint = gids[clusters[i].base_glyph + j];
390cb93a386Sopenharmony_ci      info->cluster = clusters[i].cluster;
391cb93a386Sopenharmony_ci      info->var1.i32 = clusters[i].advance;     // all glyphs in the cluster get the same advance
392cb93a386Sopenharmony_ci    }
393cb93a386Sopenharmony_ci  }
394cb93a386Sopenharmony_ci  buffer->len = glyph_count;
395cb93a386Sopenharmony_ci
396cb93a386Sopenharmony_ci  /* Positioning. */
397cb93a386Sopenharmony_ci  unsigned int currclus = UINT_MAX;
398cb93a386Sopenharmony_ci  const hb_glyph_info_t *info = buffer->info;
399cb93a386Sopenharmony_ci  hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr);
400cb93a386Sopenharmony_ci  if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
401cb93a386Sopenharmony_ci  {
402cb93a386Sopenharmony_ci    curradvx = 0;
403cb93a386Sopenharmony_ci    for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
404cb93a386Sopenharmony_ci    {
405cb93a386Sopenharmony_ci      pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx;
406cb93a386Sopenharmony_ci      pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
407cb93a386Sopenharmony_ci      if (info->cluster != currclus) {
408cb93a386Sopenharmony_ci	pPos->x_advance = info->var1.i32;
409cb93a386Sopenharmony_ci	curradvx += pPos->x_advance;
410cb93a386Sopenharmony_ci	currclus = info->cluster;
411cb93a386Sopenharmony_ci      } else
412cb93a386Sopenharmony_ci	pPos->x_advance = 0.;
413cb93a386Sopenharmony_ci
414cb93a386Sopenharmony_ci      pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
415cb93a386Sopenharmony_ci      curradvy += pPos->y_advance;
416cb93a386Sopenharmony_ci    }
417cb93a386Sopenharmony_ci  }
418cb93a386Sopenharmony_ci  else
419cb93a386Sopenharmony_ci  {
420cb93a386Sopenharmony_ci    curradvx = gr_seg_advance_X(seg) * xscale;
421cb93a386Sopenharmony_ci    for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is))
422cb93a386Sopenharmony_ci    {
423cb93a386Sopenharmony_ci      if (info->cluster != currclus)
424cb93a386Sopenharmony_ci      {
425cb93a386Sopenharmony_ci	pPos->x_advance = info->var1.i32;
426cb93a386Sopenharmony_ci	curradvx -= pPos->x_advance;
427cb93a386Sopenharmony_ci	currclus = info->cluster;
428cb93a386Sopenharmony_ci      } else
429cb93a386Sopenharmony_ci	pPos->x_advance = 0.;
430cb93a386Sopenharmony_ci
431cb93a386Sopenharmony_ci      pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
432cb93a386Sopenharmony_ci      curradvy -= pPos->y_advance;
433cb93a386Sopenharmony_ci      pPos->x_offset = gr_slot_origin_X (is) * xscale - info->var1.i32 - curradvx + pPos->x_advance;
434cb93a386Sopenharmony_ci      pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
435cb93a386Sopenharmony_ci    }
436cb93a386Sopenharmony_ci    hb_buffer_reverse_clusters (buffer);
437cb93a386Sopenharmony_ci  }
438cb93a386Sopenharmony_ci
439cb93a386Sopenharmony_ci  if (feats) gr_featureval_destroy (feats);
440cb93a386Sopenharmony_ci  gr_seg_destroy (seg);
441cb93a386Sopenharmony_ci
442cb93a386Sopenharmony_ci  buffer->unsafe_to_break_all ();
443cb93a386Sopenharmony_ci
444cb93a386Sopenharmony_ci  return true;
445cb93a386Sopenharmony_ci}
446cb93a386Sopenharmony_ci
447cb93a386Sopenharmony_ci
448cb93a386Sopenharmony_ci#endif
449