1/*
2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This is part of HarfBuzz, an OpenType Layout engine library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 */
24
25#include "harfbuzz-shaper.h"
26#include "harfbuzz-shaper-private.h"
27
28#include <assert.h>
29#include <stdio.h>
30
31enum MymrCharClassValues
32{
33    Mymr_CC_RESERVED             =  0,
34    Mymr_CC_CONSONANT            =  1, /* Consonant of type 1, that has subscript form */
35    Mymr_CC_CONSONANT2           =  2, /* Consonant of type 2, that has no subscript form */
36    Mymr_CC_NGA	          =  3, /* Consonant NGA */
37    Mymr_CC_YA		          =  4, /* Consonant YA */
38    Mymr_CC_RA		          =  5, /* Consonant RA */
39    Mymr_CC_WA		          =  6, /* Consonant WA */
40    Mymr_CC_HA		          =  7, /* Consonant HA */
41    Mymr_CC_IND_VOWEL		  =  8, /* Independent vowel */
42    Mymr_CC_ZERO_WIDTH_NJ_MARK   =  9, /* Zero Width non joiner character (0x200C) */
43    Mymr_CC_VIRAMA               = 10, /* Subscript consonant combining character */
44    Mymr_CC_PRE_VOWEL   	  = 11, /* Dependent vowel, prebase (Vowel e) */
45    Mymr_CC_BELOW_VOWEL   	  = 12, /* Dependent vowel, prebase (Vowel u, uu) */
46    Mymr_CC_ABOVE_VOWEL   	  = 13, /* Dependent vowel, prebase (Vowel i, ii, ai) */
47    Mymr_CC_POST_VOWEL   	  = 14, /* Dependent vowel, prebase (Vowel aa) */
48    Mymr_CC_SIGN_ABOVE           = 15,
49    Mymr_CC_SIGN_BELOW           = 16,
50    Mymr_CC_SIGN_AFTER           = 17,
51    Mymr_CC_ZERO_WIDTH_J_MARK    = 18, /* Zero width joiner character */
52    Mymr_CC_COUNT                = 19  /* This is the number of character classes */
53};
54
55enum MymrCharClassFlags
56{
57    Mymr_CF_CLASS_MASK    = 0x0000FFFF,
58
59    Mymr_CF_CONSONANT     = 0x01000000,  /* flag to speed up comparing */
60    Mymr_CF_MEDIAL	   = 0x02000000,  /* flag to speed up comparing */
61    Mymr_CF_IND_VOWEL 	   = 0x04000000,  /* flag to speed up comparing */
62    Mymr_CF_DEP_VOWEL 	   = 0x08000000,  /* flag to speed up comparing */
63    Mymr_CF_DOTTED_CIRCLE = 0x10000000,  /* add a dotted circle if a character with this flag is the first in a syllable */
64    Mymr_CF_VIRAMA        = 0x20000000,  /* flag to speed up comparing */
65
66    /* position flags */
67    Mymr_CF_POS_BEFORE    = 0x00080000,
68    Mymr_CF_POS_BELOW     = 0x00040000,
69    Mymr_CF_POS_ABOVE     = 0x00020000,
70    Mymr_CF_POS_AFTER     = 0x00010000,
71    Mymr_CF_POS_MASK      = 0x000f0000,
72
73    Mymr_CF_AFTER_KINZI   = 0x00100000
74};
75
76/* Characters that get refrered to by name */
77enum MymrChar
78{
79    Mymr_C_SIGN_ZWNJ     = 0x200C,
80    Mymr_C_SIGN_ZWJ      = 0x200D,
81    Mymr_C_DOTTED_CIRCLE = 0x25CC,
82    Mymr_C_RA            = 0x101B,
83    Mymr_C_YA            = 0x101A,
84    Mymr_C_NGA           = 0x1004,
85    Mymr_C_VOWEL_E       = 0x1031,
86    Mymr_C_VIRAMA        = 0x1039
87};
88
89enum
90{
91    Mymr_xx = Mymr_CC_RESERVED,
92    Mymr_c1 = Mymr_CC_CONSONANT | Mymr_CF_CONSONANT | Mymr_CF_POS_BELOW,
93    Mymr_c2 = Mymr_CC_CONSONANT2 | Mymr_CF_CONSONANT,
94    Mymr_ng = Mymr_CC_NGA | Mymr_CF_CONSONANT | Mymr_CF_POS_ABOVE,
95    Mymr_ya = Mymr_CC_YA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_AFTER | Mymr_CF_AFTER_KINZI,
96    Mymr_ra = Mymr_CC_RA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BEFORE,
97    Mymr_wa = Mymr_CC_WA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BELOW,
98    Mymr_ha = Mymr_CC_HA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BELOW,
99    Mymr_id = Mymr_CC_IND_VOWEL | Mymr_CF_IND_VOWEL,
100    Mymr_vi = Mymr_CC_VIRAMA | Mymr_CF_VIRAMA | Mymr_CF_POS_ABOVE | Mymr_CF_DOTTED_CIRCLE,
101    Mymr_dl = Mymr_CC_PRE_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_BEFORE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
102    Mymr_db = Mymr_CC_BELOW_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_BELOW | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
103    Mymr_da = Mymr_CC_ABOVE_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_ABOVE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
104    Mymr_dr = Mymr_CC_POST_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_AFTER | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
105    Mymr_sa = Mymr_CC_SIGN_ABOVE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_POS_ABOVE | Mymr_CF_AFTER_KINZI,
106    Mymr_sb = Mymr_CC_SIGN_BELOW | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_POS_BELOW | Mymr_CF_AFTER_KINZI,
107    Mymr_sp = Mymr_CC_SIGN_AFTER | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI
108};
109
110
111typedef int MymrCharClass;
112
113
114static const MymrCharClass mymrCharClasses[] =
115{
116    Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_ng, Mymr_c1, Mymr_c1, Mymr_c1,
117    Mymr_c1, Mymr_c1, Mymr_c2, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, /* 1000 - 100F */
118    Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1,
119    Mymr_c1, Mymr_c1, Mymr_ya, Mymr_ra, Mymr_c1, Mymr_wa, Mymr_c1, Mymr_ha, /* 1010 - 101F */
120    Mymr_c2, Mymr_c2, Mymr_xx, Mymr_id, Mymr_id, Mymr_id, Mymr_id, Mymr_id,
121    Mymr_xx, Mymr_id, Mymr_id, Mymr_xx, Mymr_dr, Mymr_da, Mymr_da, Mymr_db, /* 1020 - 102F */
122    Mymr_db, Mymr_dl, Mymr_da, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_sa, Mymr_sb,
123    Mymr_sp, Mymr_vi, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1030 - 103F */
124    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx,
125    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1040 - 104F */
126    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx,
127    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1050 - 105F */
128};
129
130static MymrCharClass
131getMyanmarCharClass (HB_UChar16 ch)
132{
133    if (ch == Mymr_C_SIGN_ZWJ)
134        return Mymr_CC_ZERO_WIDTH_J_MARK;
135
136    if (ch == Mymr_C_SIGN_ZWNJ)
137        return Mymr_CC_ZERO_WIDTH_NJ_MARK;
138
139    if (ch < 0x1000 || ch > 0x105f)
140        return Mymr_CC_RESERVED;
141
142    return mymrCharClasses[ch - 0x1000];
143}
144
145static const signed char mymrStateTable[][Mymr_CC_COUNT] =
146{
147/*   xx  c1, c2  ng  ya  ra  wa  ha  id zwnj vi  dl  db  da  dr  sa  sb  sp zwj */
148    { 1,  4,  4,  2,  4,  4,  4,  4, 24,  1, 27, 17, 18, 19, 20, 21,  1,  1,  4}, /*  0 - ground state */
149    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /*  1 - exit state (or sp to the right of the syllable) */
150    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  3, 17, 18, 19, 20, 21, -1, -1,  4}, /*  2 - NGA */
151    {-1,  4,  4,  4,  4,  4,  4,  4, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /*  3 - Virama after NGA */
152    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  5, 17, 18, 19, 20, 21,  1,  1, -1}, /*  4 - Base consonant */
153    {-2,  6, -2, -2,  7,  8,  9, 10, -2, 23, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /*  5 - First virama */
154    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 25, 17, 18, 19, 20, 21, -1, -1, -1}, /*  6 - c1 after virama */
155    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /*  7 - ya after virama */
156    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /*  8 - ra after virama */
157    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /*  9 - wa after virama */
158    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 10 - ha after virama */
159    {-1, -1, -1, -1,  7,  8,  9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 11 - Virama after NGA+zwj */
160    {-2, -2, -2, -2, -2, -2, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 12 - Second virama */
161    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 17, 18, 19, 20, 21, -1, -1, -1}, /* 13 - wa after virama */
162    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 14 - ha after virama */
163    {-2, -2, -2, -2, -2, -2, -2, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 15 - Third virama */
164    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 16 - ha after virama */
165    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 21,  1,  1, -1}, /* 17 - dl, Dependent vowel e */
166    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19, -1, 21,  1,  1, -1}, /* 18 - db, Dependent vowel u,uu */
167    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,  1,  1, -1}, /* 19 - da, Dependent vowel i,ii,ai */
168    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1,  1,  1, -1}, /* 20 - dr, Dependent vowel aa */
169    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,  1, -1}, /* 21 - sa, Sign anusvara */
170    {-1, -1, -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 22 - atha */
171    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,  1, -1}, /* 23 - zwnj for atha */
172    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1, -1}, /* 24 - Independent vowel */
173    {-2, -2, -2, -2, 26, 26, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 25 - Virama after subscript consonant */
174    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1,  1, -1}, /* 26 - ra/ya after subscript consonant + virama */
175    {-1,  6, -1, -1,  7,  8,  9, 10, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 27 - Virama after ground state */
176/* exit state -2 is for invalid order of medials and combination of invalids
177   with virama where virama should treat as start of next syllable
178 */
179};
180
181
182
183/*#define MYANMAR_DEBUG */
184#ifdef MYANMAR_DEBUG
185#define MMDEBUG qDebug
186#else
187#define MMDEBUG if(0) printf
188#endif
189
190/*
191//  Given an input string of characters and a location in which to start looking
192//  calculate, using the state table, which one is the last character of the syllable
193//  that starts in the starting position.
194*/
195static int myanmar_nextSyllableBoundary(const HB_UChar16 *s, int start, int end, HB_Bool *invalid)
196{
197    const HB_UChar16 *uc = s + start;
198    int state = 0;
199    int pos = start;
200    *invalid = FALSE;
201
202    while (pos < end) {
203        MymrCharClass charClass = getMyanmarCharClass(*uc);
204        state = mymrStateTable[state][charClass & Mymr_CF_CLASS_MASK];
205        if (pos == start)
206            *invalid = (HB_Bool)(charClass & Mymr_CF_DOTTED_CIRCLE);
207
208        MMDEBUG("state[%d]=%d class=%8x (uc=%4x)", pos - start, state, charClass, *uc);
209
210        if (state < 0) {
211            if (state < -1)
212                --pos;
213            break;
214        }
215        ++uc;
216        ++pos;
217    }
218    return pos;
219}
220
221#ifndef NO_OPENTYPE
222/* ###### might have to change order of above and below forms and substitutions,
223   but according to Unicode below comes before above */
224static const HB_OpenTypeFeature myanmar_features[] = {
225    { HB_MAKE_TAG('p', 'r', 'e', 'f'), PreFormProperty },
226    { HB_MAKE_TAG('b', 'l', 'w', 'f'), BelowFormProperty },
227    { HB_MAKE_TAG('a', 'b', 'v', 'f'), AboveFormProperty },
228    { HB_MAKE_TAG('p', 's', 't', 'f'), PostFormProperty },
229    { HB_MAKE_TAG('p', 'r', 'e', 's'), PreSubstProperty },
230    { HB_MAKE_TAG('b', 'l', 'w', 's'), BelowSubstProperty },
231    { HB_MAKE_TAG('a', 'b', 'v', 's'), AboveSubstProperty },
232    { HB_MAKE_TAG('p', 's', 't', 's'), PostSubstProperty },
233    { HB_MAKE_TAG('r', 'l', 'i', 'g'), CligProperty }, /* Myanmar1 uses this instead of the other features */
234    { 0, 0 }
235};
236#endif
237
238
239/*
240// Visual order before shaping should be:
241//
242//    [Vowel Mark E]
243//    [Virama + Medial Ra]
244//    [Base]
245//    [Virama + Consonant]
246//    [Nga + Virama] (Kinzi) ### should probably come before post forms (medial ya)
247//    [Vowels]
248//    [Marks]
249//
250// This means that we can keep the logical order apart from having to
251// move the pre vowel, medial ra and kinzi
252*/
253
254static HB_Bool myanmar_shape_syllable(HB_Bool openType, HB_ShaperItem *item, HB_Bool invalid)
255{
256    /*
257//    MMDEBUG("\nsyllable from %d len %d, str='%s'", item->item.pos, item->item.length,
258//	    item->string->mid(item->from, item->length).toUtf8().data());
259    */
260
261#ifndef NO_OPENTYPE
262    const int availableGlyphs = item->num_glyphs;
263#endif
264    const HB_UChar16 *uc = item->string + item->item.pos;
265    int vowel_e = -1;
266    int kinzi = -1;
267    int medial_ra = -1;
268    int base = -1;
269    int i;
270    int len = 0;
271    unsigned short reordered[32];
272    unsigned char properties[32];
273    enum {
274	AboveForm = 0x01,
275	PreForm = 0x02,
276	PostForm = 0x04,
277	BelowForm = 0x08
278    };
279    HB_Bool lastWasVirama = FALSE;
280    int basePos = -1;
281
282    memset(properties, 0, 32*sizeof(unsigned char));
283
284    /* according to the table the max length of a syllable should be around 14 chars */
285    assert(item->item.length < 32);
286
287#ifdef MYANMAR_DEBUG
288    printf("original:");
289    for (i = 0; i < (int)item->item.length; i++) {
290        printf("    %d: %4x", i, uc[i]);
291    }
292#endif
293    for (i = 0; i < (int)item->item.length; ++i) {
294        HB_UChar16 chr = uc[i];
295
296        if (chr == Mymr_C_VOWEL_E) {
297            vowel_e = i;
298            continue;
299        }
300        if (i == 0
301            && chr == Mymr_C_NGA
302            && i + 2 < (int)item->item.length
303            && uc[i+1] == Mymr_C_VIRAMA) {
304            int mc = getMyanmarCharClass(uc[i+2]);
305            /*MMDEBUG("maybe kinzi: mc=%x", mc);*/
306            if ((mc & Mymr_CF_CONSONANT) == Mymr_CF_CONSONANT) {
307                kinzi = i;
308                continue;
309            }
310        }
311        if (base >= 0
312            && chr == Mymr_C_VIRAMA
313            && i + 1 < (int)item->item.length
314            && uc[i+1] == Mymr_C_RA) {
315            medial_ra = i;
316            continue;
317        }
318        if (base < 0)
319            base = i;
320    }
321
322    MMDEBUG("\n  base=%d, vowel_e=%d, kinzi=%d, medial_ra=%d", base, vowel_e, kinzi, medial_ra);
323    /* write vowel_e if found */
324    if (vowel_e >= 0) {
325        reordered[0] = Mymr_C_VOWEL_E;
326        len = 1;
327    }
328    /* write medial_ra */
329    if (medial_ra >= 0) {
330        reordered[len] = Mymr_C_VIRAMA;
331        reordered[len+1] = Mymr_C_RA;
332        properties[len] = PreForm;
333        properties[len+1] = PreForm;
334        len += 2;
335    }
336
337    /* shall we add a dotted circle?
338       If in the position in which the base should be (first char in the string) there is
339       a character that has the Dotted circle flag (a character that cannot be a base)
340       then write a dotted circle */
341    if (invalid) {
342        reordered[len] = C_DOTTED_CIRCLE;
343        ++len;
344    }
345
346    /* copy the rest of the syllable to the output, inserting the kinzi
347       at the correct place */
348    for (i = 0; i < (int)item->item.length; ++i) {
349        hb_uint16 chr = uc[i];
350        MymrCharClass cc;
351        if (i == vowel_e)
352            continue;
353        if (i == medial_ra || i == kinzi) {
354            ++i;
355            continue;
356        }
357
358        cc = getMyanmarCharClass(uc[i]);
359        if (kinzi >= 0 && i > base && (cc & Mymr_CF_AFTER_KINZI)) {
360            reordered[len] = Mymr_C_NGA;
361            reordered[len+1] = Mymr_C_VIRAMA;
362            properties[len-1] = AboveForm;
363            properties[len] = AboveForm;
364            len += 2;
365            kinzi = -1;
366        }
367
368        if (lastWasVirama) {
369            int prop = 0;
370            switch(cc & Mymr_CF_POS_MASK) {
371            case Mymr_CF_POS_BEFORE:
372                prop = PreForm;
373                break;
374            case Mymr_CF_POS_BELOW:
375                prop = BelowForm;
376                break;
377            case Mymr_CF_POS_ABOVE:
378                prop = AboveForm;
379                break;
380            case Mymr_CF_POS_AFTER:
381                prop = PostForm;
382                break;
383            default:
384                break;
385            }
386            properties[len-1] = prop;
387            properties[len] = prop;
388            if(basePos >= 0 && basePos == len-2)
389                properties[len-2] = prop;
390        }
391        lastWasVirama = (chr == Mymr_C_VIRAMA);
392        if(i == base)
393            basePos = len;
394
395        if ((chr != Mymr_C_SIGN_ZWNJ && chr != Mymr_C_SIGN_ZWJ) || !len) {
396            reordered[len] = chr;
397            ++len;
398        }
399    }
400    if (kinzi >= 0) {
401        reordered[len] = Mymr_C_NGA;
402        reordered[len+1] = Mymr_C_VIRAMA;
403        properties[len] = AboveForm;
404        properties[len+1] = AboveForm;
405        len += 2;
406    }
407
408    if (!item->font->klass->convertStringToGlyphIndices(item->font,
409                                                        reordered, len,
410                                                        item->glyphs, &item->num_glyphs,
411                                                        item->item.bidiLevel % 2))
412        return FALSE;
413
414    MMDEBUG("after shaping: len=%d", len);
415    for (i = 0; i < len; i++) {
416	item->attributes[i].mark = FALSE;
417	item->attributes[i].clusterStart = FALSE;
418	item->attributes[i].justification = 0;
419	item->attributes[i].zeroWidth = FALSE;
420	MMDEBUG("    %d: %4x property=%x", i, reordered[i], properties[i]);
421    }
422
423    /* now we have the syllable in the right order, and can start running it through open type. */
424
425#ifndef NO_OPENTYPE
426    if (openType) {
427 	hb_uint32 where[32];
428
429        for (i = 0; i < len; ++i) {
430            where[i] = ~(PreSubstProperty
431                         | BelowSubstProperty
432                         | AboveSubstProperty
433                         | PostSubstProperty
434                         | CligProperty
435                         | PositioningProperties);
436            if (properties[i] & PreForm)
437                where[i] &= ~PreFormProperty;
438            if (properties[i] & BelowForm)
439                where[i] &= ~BelowFormProperty;
440            if (properties[i] & AboveForm)
441                where[i] &= ~AboveFormProperty;
442            if (properties[i] & PostForm)
443                where[i] &= ~PostFormProperty;
444        }
445
446        HB_OpenTypeShape(item, where);
447        if (!HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE))
448            return FALSE;
449    } else
450#endif
451    {
452	MMDEBUG("Not using openType");
453        HB_HeuristicPosition(item);
454    }
455
456    item->attributes[0].clusterStart = TRUE;
457    return TRUE;
458}
459
460HB_Bool HB_MyanmarShape(HB_ShaperItem *item)
461{
462    HB_Bool openType = FALSE;
463    unsigned short *logClusters = item->log_clusters;
464
465    HB_ShaperItem syllable = *item;
466    int first_glyph = 0;
467
468    int sstart = item->item.pos;
469    int end = sstart + item->item.length;
470    int i = 0;
471
472    assert(item->item.script == HB_Script_Myanmar);
473#ifndef NO_OPENTYPE
474    openType = HB_SelectScript(item, myanmar_features);
475#endif
476
477    MMDEBUG("myanmar_shape: from %d length %d", item->item.pos, item->item.length);
478    while (sstart < end) {
479        HB_Bool invalid;
480        int send = myanmar_nextSyllableBoundary(item->string, sstart, end, &invalid);
481        MMDEBUG("syllable from %d, length %d, invalid=%s", sstart, send-sstart,
482               invalid ? "TRUE" : "FALSE");
483        syllable.item.pos = sstart;
484        syllable.item.length = send-sstart;
485        syllable.glyphs = item->glyphs + first_glyph;
486        syllable.attributes = item->attributes + first_glyph;
487        syllable.advances = item->advances + first_glyph;
488        syllable.offsets = item->offsets + first_glyph;
489        syllable.num_glyphs = item->num_glyphs - first_glyph;
490        if (!myanmar_shape_syllable(openType, &syllable, invalid)) {
491            MMDEBUG("syllable shaping failed, syllable requests %d glyphs", syllable.num_glyphs);
492            item->num_glyphs += syllable.num_glyphs;
493            return FALSE;
494        }
495
496        /* fix logcluster array */
497        MMDEBUG("syllable:");
498        for (i = first_glyph; i < first_glyph + (int)syllable.num_glyphs; ++i)
499            MMDEBUG("        %d -> glyph %x", i, item->glyphs[i]);
500        MMDEBUG("    logclusters:");
501        for (i = sstart; i < send; ++i) {
502            MMDEBUG("        %d -> glyph %d", i, first_glyph);
503            logClusters[i-item->item.pos] = first_glyph;
504        }
505        sstart = send;
506        first_glyph += syllable.num_glyphs;
507    }
508    item->num_glyphs = first_glyph;
509    return TRUE;
510}
511
512void HB_MyanmarAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
513{
514    int end = from + len;
515    const HB_UChar16 *uc = text + from;
516    hb_uint32 i = 0;
517    HB_UNUSED(script);
518    attributes += from;
519    while (i < len) {
520	HB_Bool invalid;
521	hb_uint32 boundary = myanmar_nextSyllableBoundary(text, from+i, end, &invalid) - from;
522
523	attributes[i].charStop = TRUE;
524        if (i)
525            attributes[i-1].lineBreakType = HB_Break;
526
527	if (boundary > len-1)
528            boundary = len;
529	i++;
530	while (i < boundary) {
531	    attributes[i].charStop = FALSE;
532	    ++uc;
533	    ++i;
534	}
535	assert(i == boundary);
536    }
537}
538
539