15569331642446be05292e3e1f8a51218827168cdclaireho/*
25569331642446be05292e3e1f8a51218827168cdclaireho * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
35569331642446be05292e3e1f8a51218827168cdclaireho *
45569331642446be05292e3e1f8a51218827168cdclaireho * This is part of HarfBuzz, an OpenType Layout engine library.
55569331642446be05292e3e1f8a51218827168cdclaireho *
65569331642446be05292e3e1f8a51218827168cdclaireho * Permission is hereby granted, without written agreement and without
75569331642446be05292e3e1f8a51218827168cdclaireho * license or royalty fees, to use, copy, modify, and distribute this
85569331642446be05292e3e1f8a51218827168cdclaireho * software and its documentation for any purpose, provided that the
95569331642446be05292e3e1f8a51218827168cdclaireho * above copyright notice and the following two paragraphs appear in
105569331642446be05292e3e1f8a51218827168cdclaireho * all copies of this software.
115569331642446be05292e3e1f8a51218827168cdclaireho *
125569331642446be05292e3e1f8a51218827168cdclaireho * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
135569331642446be05292e3e1f8a51218827168cdclaireho * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
145569331642446be05292e3e1f8a51218827168cdclaireho * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
155569331642446be05292e3e1f8a51218827168cdclaireho * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
165569331642446be05292e3e1f8a51218827168cdclaireho * DAMAGE.
175569331642446be05292e3e1f8a51218827168cdclaireho *
185569331642446be05292e3e1f8a51218827168cdclaireho * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
195569331642446be05292e3e1f8a51218827168cdclaireho * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
205569331642446be05292e3e1f8a51218827168cdclaireho * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
215569331642446be05292e3e1f8a51218827168cdclaireho * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
225569331642446be05292e3e1f8a51218827168cdclaireho * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
235569331642446be05292e3e1f8a51218827168cdclaireho */
245569331642446be05292e3e1f8a51218827168cdclaireho
255569331642446be05292e3e1f8a51218827168cdclaireho#include "harfbuzz-shaper.h"
265569331642446be05292e3e1f8a51218827168cdclaireho#include "harfbuzz-shaper-private.h"
275569331642446be05292e3e1f8a51218827168cdclaireho
285569331642446be05292e3e1f8a51218827168cdclaireho#include <assert.h>
295569331642446be05292e3e1f8a51218827168cdclaireho
305569331642446be05292e3e1f8a51218827168cdclaireho/*
315569331642446be05292e3e1f8a51218827168cdclaireho// Hangul is a syllable based script. Unicode reserves a large range
325569331642446be05292e3e1f8a51218827168cdclaireho// for precomposed hangul, where syllables are already precomposed to
335569331642446be05292e3e1f8a51218827168cdclaireho// their final glyph shape. In addition, a so called jamo range is
345569331642446be05292e3e1f8a51218827168cdclaireho// defined, that can be used to express old Hangul. Modern hangul
355569331642446be05292e3e1f8a51218827168cdclaireho// syllables can also be expressed as jamo, and should be composed
365569331642446be05292e3e1f8a51218827168cdclaireho// into syllables. The operation is rather simple and mathematical.
375569331642446be05292e3e1f8a51218827168cdclaireho
385569331642446be05292e3e1f8a51218827168cdclaireho// Every hangul jamo is classified as being either a Leading consonant
395569331642446be05292e3e1f8a51218827168cdclaireho// (L), and intermediat Vowel (V) or a trailing consonant (T). Modern
405569331642446be05292e3e1f8a51218827168cdclaireho// hangul syllables (the ones in the precomposed area can be of type
415569331642446be05292e3e1f8a51218827168cdclaireho// LV or LVT.
425569331642446be05292e3e1f8a51218827168cdclaireho//
435569331642446be05292e3e1f8a51218827168cdclaireho// Syllable breaks do _not_ occur between:
445569331642446be05292e3e1f8a51218827168cdclaireho//
455569331642446be05292e3e1f8a51218827168cdclaireho// L              L, V or precomposed
465569331642446be05292e3e1f8a51218827168cdclaireho// V, LV          V, T
475569331642446be05292e3e1f8a51218827168cdclaireho// LVT, T         T
485569331642446be05292e3e1f8a51218827168cdclaireho//
495569331642446be05292e3e1f8a51218827168cdclaireho// A standard syllable is of the form L+V+T*. The above rules allow
505569331642446be05292e3e1f8a51218827168cdclaireho// nonstandard syllables L*V*T*. To transform them into standard
515569331642446be05292e3e1f8a51218827168cdclaireho// syllables fill characters L_f and V_f can be inserted.
525569331642446be05292e3e1f8a51218827168cdclaireho*/
535569331642446be05292e3e1f8a51218827168cdclaireho
545569331642446be05292e3e1f8a51218827168cdclairehoenum {
555569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_SBase = 0xac00,
565569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_LBase = 0x1100,
575569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_VBase = 0x1161,
585569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_TBase = 0x11a7,
595569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_SCount = 11172,
605569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_LCount = 19,
615569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_VCount = 21,
625569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_TCount = 28,
635569331642446be05292e3e1f8a51218827168cdclaireho    Hangul_NCount = 21*28
645569331642446be05292e3e1f8a51218827168cdclaireho};
655569331642446be05292e3e1f8a51218827168cdclaireho
665569331642446be05292e3e1f8a51218827168cdclaireho#define hangul_isPrecomposed(uc) \
675569331642446be05292e3e1f8a51218827168cdclaireho    (uc >= Hangul_SBase && uc < Hangul_SBase + Hangul_SCount)
685569331642446be05292e3e1f8a51218827168cdclaireho
695569331642446be05292e3e1f8a51218827168cdclaireho#define hangul_isLV(uc) \
705569331642446be05292e3e1f8a51218827168cdclaireho    ((uc - Hangul_SBase) % Hangul_TCount == 0)
715569331642446be05292e3e1f8a51218827168cdclaireho
725569331642446be05292e3e1f8a51218827168cdclairehotypedef enum {
735569331642446be05292e3e1f8a51218827168cdclaireho    L,
745569331642446be05292e3e1f8a51218827168cdclaireho    V,
755569331642446be05292e3e1f8a51218827168cdclaireho    T,
765569331642446be05292e3e1f8a51218827168cdclaireho    LV,
775569331642446be05292e3e1f8a51218827168cdclaireho    LVT,
785569331642446be05292e3e1f8a51218827168cdclaireho    X
795569331642446be05292e3e1f8a51218827168cdclaireho} HangulType;
805569331642446be05292e3e1f8a51218827168cdclaireho
815569331642446be05292e3e1f8a51218827168cdclairehostatic HangulType hangul_type(unsigned short uc) {
825569331642446be05292e3e1f8a51218827168cdclaireho    if (uc > Hangul_SBase && uc < Hangul_SBase + Hangul_SCount)
835569331642446be05292e3e1f8a51218827168cdclaireho        return hangul_isLV(uc) ? LV : LVT;
845569331642446be05292e3e1f8a51218827168cdclaireho    if (uc < Hangul_LBase || uc > 0x11ff)
855569331642446be05292e3e1f8a51218827168cdclaireho        return X;
865569331642446be05292e3e1f8a51218827168cdclaireho    if (uc < Hangul_VBase)
875569331642446be05292e3e1f8a51218827168cdclaireho        return L;
885569331642446be05292e3e1f8a51218827168cdclaireho    if (uc < Hangul_TBase)
895569331642446be05292e3e1f8a51218827168cdclaireho        return V;
905569331642446be05292e3e1f8a51218827168cdclaireho    return T;
915569331642446be05292e3e1f8a51218827168cdclaireho}
925569331642446be05292e3e1f8a51218827168cdclaireho
935569331642446be05292e3e1f8a51218827168cdclairehostatic int hangul_nextSyllableBoundary(const HB_UChar16 *s, int start, int end)
945569331642446be05292e3e1f8a51218827168cdclaireho{
955569331642446be05292e3e1f8a51218827168cdclaireho    const HB_UChar16 *uc = s + start;
965569331642446be05292e3e1f8a51218827168cdclaireho
975569331642446be05292e3e1f8a51218827168cdclaireho    HangulType state = hangul_type(*uc);
985569331642446be05292e3e1f8a51218827168cdclaireho    int pos = 1;
995569331642446be05292e3e1f8a51218827168cdclaireho
1005569331642446be05292e3e1f8a51218827168cdclaireho    while (pos < end - start) {
1015569331642446be05292e3e1f8a51218827168cdclaireho        HangulType newState = hangul_type(uc[pos]);
1025569331642446be05292e3e1f8a51218827168cdclaireho        switch(newState) {
1035569331642446be05292e3e1f8a51218827168cdclaireho        case X:
1045569331642446be05292e3e1f8a51218827168cdclaireho            goto finish;
1055569331642446be05292e3e1f8a51218827168cdclaireho        case L:
1065569331642446be05292e3e1f8a51218827168cdclaireho        case V:
1075569331642446be05292e3e1f8a51218827168cdclaireho        case T:
1085569331642446be05292e3e1f8a51218827168cdclaireho            if (state > newState)
1095569331642446be05292e3e1f8a51218827168cdclaireho                goto finish;
1105569331642446be05292e3e1f8a51218827168cdclaireho            state = newState;
1115569331642446be05292e3e1f8a51218827168cdclaireho            break;
1125569331642446be05292e3e1f8a51218827168cdclaireho        case LV:
1135569331642446be05292e3e1f8a51218827168cdclaireho            if (state > L)
1145569331642446be05292e3e1f8a51218827168cdclaireho                goto finish;
1155569331642446be05292e3e1f8a51218827168cdclaireho            state = V;
1165569331642446be05292e3e1f8a51218827168cdclaireho            break;
1175569331642446be05292e3e1f8a51218827168cdclaireho        case LVT:
1185569331642446be05292e3e1f8a51218827168cdclaireho            if (state > L)
1195569331642446be05292e3e1f8a51218827168cdclaireho                goto finish;
1205569331642446be05292e3e1f8a51218827168cdclaireho            state = T;
1215569331642446be05292e3e1f8a51218827168cdclaireho        }
1225569331642446be05292e3e1f8a51218827168cdclaireho        ++pos;
1235569331642446be05292e3e1f8a51218827168cdclaireho    }
1245569331642446be05292e3e1f8a51218827168cdclaireho
1255569331642446be05292e3e1f8a51218827168cdclaireho finish:
1265569331642446be05292e3e1f8a51218827168cdclaireho    return start+pos;
1275569331642446be05292e3e1f8a51218827168cdclaireho}
1285569331642446be05292e3e1f8a51218827168cdclaireho
1295569331642446be05292e3e1f8a51218827168cdclaireho#ifndef NO_OPENTYPE
1305569331642446be05292e3e1f8a51218827168cdclairehostatic const HB_OpenTypeFeature hangul_features [] = {
1315569331642446be05292e3e1f8a51218827168cdclaireho    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
1325569331642446be05292e3e1f8a51218827168cdclaireho    { HB_MAKE_TAG('l', 'j', 'm', 'o'), CcmpProperty },
13357e6107a9d66a9a97b146def0ef38c010f954be6claireho    { HB_MAKE_TAG('v', 'j', 'm', 'o'), CcmpProperty },
1345569331642446be05292e3e1f8a51218827168cdclaireho    { HB_MAKE_TAG('t', 'j', 'm', 'o'), CcmpProperty },
1355569331642446be05292e3e1f8a51218827168cdclaireho    { 0, 0 }
1365569331642446be05292e3e1f8a51218827168cdclaireho};
1375569331642446be05292e3e1f8a51218827168cdclaireho#endif
1385569331642446be05292e3e1f8a51218827168cdclaireho
1395569331642446be05292e3e1f8a51218827168cdclairehostatic HB_Bool hangul_shape_syllable(HB_ShaperItem *item, HB_Bool openType)
1405569331642446be05292e3e1f8a51218827168cdclaireho{
1415569331642446be05292e3e1f8a51218827168cdclaireho    const HB_UChar16 *ch = item->string + item->item.pos;
1425569331642446be05292e3e1f8a51218827168cdclaireho    int len = item->item.length;
1435569331642446be05292e3e1f8a51218827168cdclaireho#ifndef NO_OPENTYPE
1445569331642446be05292e3e1f8a51218827168cdclaireho    const int availableGlyphs = item->num_glyphs;
1455569331642446be05292e3e1f8a51218827168cdclaireho#endif
1465569331642446be05292e3e1f8a51218827168cdclaireho
1475569331642446be05292e3e1f8a51218827168cdclaireho    int i;
1485569331642446be05292e3e1f8a51218827168cdclaireho    HB_UChar16 composed = 0;
1495569331642446be05292e3e1f8a51218827168cdclaireho    /* see if we can compose the syllable into a modern hangul */
1505569331642446be05292e3e1f8a51218827168cdclaireho    if (item->item.length == 2) {
1515569331642446be05292e3e1f8a51218827168cdclaireho        int LIndex = ch[0] - Hangul_LBase;
1525569331642446be05292e3e1f8a51218827168cdclaireho        int VIndex = ch[1] - Hangul_VBase;
1535569331642446be05292e3e1f8a51218827168cdclaireho        if (LIndex >= 0 && LIndex < Hangul_LCount &&
1545569331642446be05292e3e1f8a51218827168cdclaireho            VIndex >= 0 && VIndex < Hangul_VCount)
1555569331642446be05292e3e1f8a51218827168cdclaireho            composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + Hangul_SBase;
1565569331642446be05292e3e1f8a51218827168cdclaireho    } else if (item->item.length == 3) {
1575569331642446be05292e3e1f8a51218827168cdclaireho        int LIndex = ch[0] - Hangul_LBase;
1585569331642446be05292e3e1f8a51218827168cdclaireho        int VIndex = ch[1] - Hangul_VBase;
1595569331642446be05292e3e1f8a51218827168cdclaireho        int TIndex = ch[2] - Hangul_TBase;
1605569331642446be05292e3e1f8a51218827168cdclaireho        if (LIndex >= 0 && LIndex < Hangul_LCount &&
1615569331642446be05292e3e1f8a51218827168cdclaireho            VIndex >= 0 && VIndex < Hangul_VCount &&
1625569331642446be05292e3e1f8a51218827168cdclaireho            TIndex >= 0 && TIndex < Hangul_TCount)
1635569331642446be05292e3e1f8a51218827168cdclaireho            composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + TIndex + Hangul_SBase;
1645569331642446be05292e3e1f8a51218827168cdclaireho    }
1655569331642446be05292e3e1f8a51218827168cdclaireho
1665569331642446be05292e3e1f8a51218827168cdclaireho
1675569331642446be05292e3e1f8a51218827168cdclaireho
1685569331642446be05292e3e1f8a51218827168cdclaireho    /* if we have a modern hangul use the composed form */
1695569331642446be05292e3e1f8a51218827168cdclaireho    if (composed) {
1705569331642446be05292e3e1f8a51218827168cdclaireho        ch = &composed;
1715569331642446be05292e3e1f8a51218827168cdclaireho        len = 1;
1725569331642446be05292e3e1f8a51218827168cdclaireho    }
1735569331642446be05292e3e1f8a51218827168cdclaireho
1745569331642446be05292e3e1f8a51218827168cdclaireho    if (!item->font->klass->convertStringToGlyphIndices(item->font,
1755569331642446be05292e3e1f8a51218827168cdclaireho                                                        ch, len,
1765569331642446be05292e3e1f8a51218827168cdclaireho                                                        item->glyphs, &item->num_glyphs,
1775569331642446be05292e3e1f8a51218827168cdclaireho                                                        item->item.bidiLevel % 2))
1785569331642446be05292e3e1f8a51218827168cdclaireho        return FALSE;
1795569331642446be05292e3e1f8a51218827168cdclaireho    for (i = 0; i < len; i++) {
1805569331642446be05292e3e1f8a51218827168cdclaireho        item->attributes[i].mark = FALSE;
1815569331642446be05292e3e1f8a51218827168cdclaireho        item->attributes[i].clusterStart = FALSE;
1825569331642446be05292e3e1f8a51218827168cdclaireho        item->attributes[i].justification = 0;
1835569331642446be05292e3e1f8a51218827168cdclaireho        item->attributes[i].zeroWidth = FALSE;
1845569331642446be05292e3e1f8a51218827168cdclaireho        /*IDEBUG("    %d: %4x", i, ch[i].unicode()); */
1855569331642446be05292e3e1f8a51218827168cdclaireho    }
1865569331642446be05292e3e1f8a51218827168cdclaireho
1875569331642446be05292e3e1f8a51218827168cdclaireho#ifndef NO_OPENTYPE
1885569331642446be05292e3e1f8a51218827168cdclaireho    if (!composed && openType) {
1895569331642446be05292e3e1f8a51218827168cdclaireho        HB_Bool positioned;
1905569331642446be05292e3e1f8a51218827168cdclaireho
1915569331642446be05292e3e1f8a51218827168cdclaireho        HB_STACKARRAY(unsigned short, logClusters, len);
1925569331642446be05292e3e1f8a51218827168cdclaireho        for (i = 0; i < len; ++i)
1935569331642446be05292e3e1f8a51218827168cdclaireho            logClusters[i] = i;
1945569331642446be05292e3e1f8a51218827168cdclaireho        item->log_clusters = logClusters;
1955569331642446be05292e3e1f8a51218827168cdclaireho
1965569331642446be05292e3e1f8a51218827168cdclaireho        HB_OpenTypeShape(item, /*properties*/0);
1975569331642446be05292e3e1f8a51218827168cdclaireho
1985569331642446be05292e3e1f8a51218827168cdclaireho        positioned = HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE);
1995569331642446be05292e3e1f8a51218827168cdclaireho
2005569331642446be05292e3e1f8a51218827168cdclaireho        HB_FREE_STACKARRAY(logClusters);
2015569331642446be05292e3e1f8a51218827168cdclaireho
2025569331642446be05292e3e1f8a51218827168cdclaireho        if (!positioned)
2035569331642446be05292e3e1f8a51218827168cdclaireho            return FALSE;
2045569331642446be05292e3e1f8a51218827168cdclaireho    } else {
2055569331642446be05292e3e1f8a51218827168cdclaireho        HB_HeuristicPosition(item);
2065569331642446be05292e3e1f8a51218827168cdclaireho    }
2075569331642446be05292e3e1f8a51218827168cdclaireho#endif
2085569331642446be05292e3e1f8a51218827168cdclaireho
2095569331642446be05292e3e1f8a51218827168cdclaireho    item->attributes[0].clusterStart = TRUE;
2105569331642446be05292e3e1f8a51218827168cdclaireho    return TRUE;
2115569331642446be05292e3e1f8a51218827168cdclaireho}
2125569331642446be05292e3e1f8a51218827168cdclaireho
2135569331642446be05292e3e1f8a51218827168cdclairehoHB_Bool HB_HangulShape(HB_ShaperItem *item)
2145569331642446be05292e3e1f8a51218827168cdclaireho{
2155569331642446be05292e3e1f8a51218827168cdclaireho    const HB_UChar16 *uc = item->string + item->item.pos;
2165569331642446be05292e3e1f8a51218827168cdclaireho    HB_Bool allPrecomposed = TRUE;
2175569331642446be05292e3e1f8a51218827168cdclaireho    int i;
2185569331642446be05292e3e1f8a51218827168cdclaireho
2195569331642446be05292e3e1f8a51218827168cdclaireho    assert(item->item.script == HB_Script_Hangul);
2205569331642446be05292e3e1f8a51218827168cdclaireho
2215569331642446be05292e3e1f8a51218827168cdclaireho    for (i = 0; i < (int)item->item.length; ++i) {
2225569331642446be05292e3e1f8a51218827168cdclaireho        if (!hangul_isPrecomposed(uc[i])) {
2235569331642446be05292e3e1f8a51218827168cdclaireho            allPrecomposed = FALSE;
2245569331642446be05292e3e1f8a51218827168cdclaireho            break;
2255569331642446be05292e3e1f8a51218827168cdclaireho        }
2265569331642446be05292e3e1f8a51218827168cdclaireho    }
2275569331642446be05292e3e1f8a51218827168cdclaireho
2285569331642446be05292e3e1f8a51218827168cdclaireho    if (!allPrecomposed) {
2295569331642446be05292e3e1f8a51218827168cdclaireho        HB_Bool openType = FALSE;
2305569331642446be05292e3e1f8a51218827168cdclaireho        unsigned short *logClusters = item->log_clusters;
2315569331642446be05292e3e1f8a51218827168cdclaireho        HB_ShaperItem syllable;
2325569331642446be05292e3e1f8a51218827168cdclaireho        int first_glyph = 0;
2335569331642446be05292e3e1f8a51218827168cdclaireho        int sstart = item->item.pos;
2345569331642446be05292e3e1f8a51218827168cdclaireho        int end = sstart + item->item.length;
2355569331642446be05292e3e1f8a51218827168cdclaireho
2365569331642446be05292e3e1f8a51218827168cdclaireho#ifndef NO_OPENTYPE
2375569331642446be05292e3e1f8a51218827168cdclaireho        openType = HB_SelectScript(item, hangul_features);
2385569331642446be05292e3e1f8a51218827168cdclaireho#endif
2395569331642446be05292e3e1f8a51218827168cdclaireho        syllable = *item;
2405569331642446be05292e3e1f8a51218827168cdclaireho
2415569331642446be05292e3e1f8a51218827168cdclaireho        while (sstart < end) {
2425569331642446be05292e3e1f8a51218827168cdclaireho            int send = hangul_nextSyllableBoundary(item->string, sstart, end);
2435569331642446be05292e3e1f8a51218827168cdclaireho
2445569331642446be05292e3e1f8a51218827168cdclaireho            syllable.item.pos = sstart;
2455569331642446be05292e3e1f8a51218827168cdclaireho            syllable.item.length = send-sstart;
2465569331642446be05292e3e1f8a51218827168cdclaireho            syllable.glyphs = item->glyphs + first_glyph;
2475569331642446be05292e3e1f8a51218827168cdclaireho            syllable.attributes = item->attributes + first_glyph;
2485569331642446be05292e3e1f8a51218827168cdclaireho            syllable.offsets = item->offsets + first_glyph;
2495569331642446be05292e3e1f8a51218827168cdclaireho            syllable.advances = item->advances + first_glyph;
2505569331642446be05292e3e1f8a51218827168cdclaireho            syllable.num_glyphs = item->num_glyphs - first_glyph;
2515569331642446be05292e3e1f8a51218827168cdclaireho            if (!hangul_shape_syllable(&syllable, openType)) {
2525569331642446be05292e3e1f8a51218827168cdclaireho                item->num_glyphs += syllable.num_glyphs;
2535569331642446be05292e3e1f8a51218827168cdclaireho                return FALSE;
2545569331642446be05292e3e1f8a51218827168cdclaireho            }
2555569331642446be05292e3e1f8a51218827168cdclaireho            /* fix logcluster array */
2565569331642446be05292e3e1f8a51218827168cdclaireho            for (i = sstart; i < send; ++i)
2575569331642446be05292e3e1f8a51218827168cdclaireho                logClusters[i-item->item.pos] = first_glyph;
2585569331642446be05292e3e1f8a51218827168cdclaireho            sstart = send;
2595569331642446be05292e3e1f8a51218827168cdclaireho            first_glyph += syllable.num_glyphs;
2605569331642446be05292e3e1f8a51218827168cdclaireho        }
2615569331642446be05292e3e1f8a51218827168cdclaireho        item->num_glyphs = first_glyph;
2625569331642446be05292e3e1f8a51218827168cdclaireho        return TRUE;
2635569331642446be05292e3e1f8a51218827168cdclaireho    }
2645569331642446be05292e3e1f8a51218827168cdclaireho
2655569331642446be05292e3e1f8a51218827168cdclaireho    return HB_BasicShape(item);
2665569331642446be05292e3e1f8a51218827168cdclaireho}
2675569331642446be05292e3e1f8a51218827168cdclaireho
2685569331642446be05292e3e1f8a51218827168cdclaireho
269