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
30/*
31 tibetan syllables are of the form:
32    head position consonant
33    first sub-joined consonant
34    ....intermediate sub-joined consonants (if any)
35    last sub-joined consonant
36    sub-joined vowel (a-chung U+0F71)
37    standard or compound vowel sign (or 'virama' for devanagari transliteration)
38*/
39
40typedef enum {
41    TibetanOther,
42    TibetanHeadConsonant,
43    TibetanSubjoinedConsonant,
44    TibetanSubjoinedVowel,
45    TibetanVowel
46} TibetanForm;
47
48/* this table starts at U+0f40 */
49static const unsigned char tibetanForm[0x80] = {
50    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
51    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
52    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
53    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
54
55    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
56    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
57    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
58    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
59
60    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
61    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
62    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
63    TibetanOther, TibetanOther, TibetanOther, TibetanOther,
64
65    TibetanOther, TibetanVowel, TibetanVowel, TibetanVowel,
66    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
67    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
68    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
69
70    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
71    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
72    TibetanOther, TibetanOther, TibetanOther, TibetanOther,
73    TibetanOther, TibetanOther, TibetanOther, TibetanOther,
74
75    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
76    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
77    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
78    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
79
80    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
81    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
82    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
83    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
84
85    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
86    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
87    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
88    TibetanSubjoinedConsonant, TibetanOther, TibetanOther, TibetanOther
89};
90
91
92#define tibetan_form(c) \
93    ((c) >= 0x0f40 && (c) < 0x0fc0 ? (TibetanForm)tibetanForm[(c) - 0x0f40] : TibetanOther)
94
95static const HB_OpenTypeFeature tibetan_features[] = {
96    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
97    { HB_MAKE_TAG('a', 'b', 'v', 's'), AboveSubstProperty },
98    { HB_MAKE_TAG('b', 'l', 'w', 's'), BelowSubstProperty },
99    { HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
100    {0, 0}
101};
102
103static HB_Bool tibetan_shape_syllable(HB_Bool openType, HB_ShaperItem *item, HB_Bool invalid)
104{
105    hb_uint32 i;
106    const HB_UChar16 *str = item->string + item->item.pos;
107    int len = item->item.length;
108#ifndef NO_OPENTYPE
109    const int availableGlyphs = item->num_glyphs;
110#endif
111    HB_Bool haveGlyphs;
112    HB_STACKARRAY(HB_UChar16, reordered, len + 4);
113
114    if (item->num_glyphs < item->item.length + 4) {
115        item->num_glyphs = item->item.length + 4;
116        HB_FREE_STACKARRAY(reordered);
117        return FALSE;
118    }
119
120    if (invalid) {
121        *reordered = 0x25cc;
122        memcpy(reordered+1, str, len*sizeof(HB_UChar16));
123        len++;
124        str = reordered;
125    }
126
127    haveGlyphs = item->font->klass->convertStringToGlyphIndices(item->font,
128                                                                str, len,
129                                                                item->glyphs, &item->num_glyphs,
130                                                                item->item.bidiLevel % 2);
131
132    HB_FREE_STACKARRAY(reordered);
133
134    if (!haveGlyphs)
135        return FALSE;
136
137    for (i = 0; i < item->item.length; i++) {
138        item->attributes[i].mark = FALSE;
139        item->attributes[i].clusterStart = FALSE;
140        item->attributes[i].justification = 0;
141        item->attributes[i].zeroWidth = FALSE;
142/*        IDEBUG("    %d: %4x", i, str[i]); */
143    }
144
145    /* now we have the syllable in the right order, and can start running it through open type. */
146
147#ifndef NO_OPENTYPE
148    if (openType) {
149        HB_OpenTypeShape(item, /*properties*/0);
150        if (!HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE))
151            return FALSE;
152    } else {
153        HB_HeuristicPosition(item);
154    }
155#endif
156
157    item->attributes[0].clusterStart = TRUE;
158    return TRUE;
159}
160
161
162static int tibetan_nextSyllableBoundary(const HB_UChar16 *s, int start, int end, HB_Bool *invalid)
163{
164    const HB_UChar16 *uc = s + start;
165
166    int pos = 0;
167    TibetanForm state = tibetan_form(*uc);
168
169/*     qDebug("state[%d]=%d (uc=%4x)", pos, state, uc[pos]);*/
170    pos++;
171
172    if (state != TibetanHeadConsonant) {
173        if (state != TibetanOther)
174            *invalid = TRUE;
175        goto finish;
176    }
177
178    while (pos < end - start) {
179        TibetanForm newState = tibetan_form(uc[pos]);
180        switch(newState) {
181        case TibetanSubjoinedConsonant:
182        case TibetanSubjoinedVowel:
183            if (state != TibetanHeadConsonant &&
184                 state != TibetanSubjoinedConsonant)
185                goto finish;
186            state = newState;
187            break;
188        case TibetanVowel:
189            if (state != TibetanHeadConsonant &&
190                 state != TibetanSubjoinedConsonant &&
191                 state != TibetanSubjoinedVowel)
192                goto finish;
193            break;
194        case TibetanOther:
195        case TibetanHeadConsonant:
196            goto finish;
197        }
198        pos++;
199    }
200
201finish:
202    *invalid = FALSE;
203    return start+pos;
204}
205
206HB_Bool HB_TibetanShape(HB_ShaperItem *item)
207{
208
209    HB_Bool openType = FALSE;
210    unsigned short *logClusters = item->log_clusters;
211
212    HB_ShaperItem syllable = *item;
213    int first_glyph = 0;
214
215    int sstart = item->item.pos;
216    int end = sstart + item->item.length;
217
218    assert(item->item.script == HB_Script_Tibetan);
219
220#ifndef QT_NO_OPENTYPE
221    openType = HB_SelectScript(item, tibetan_features);
222#endif
223
224    while (sstart < end) {
225        HB_Bool invalid;
226        int i;
227        int send = tibetan_nextSyllableBoundary(item->string, sstart, end, &invalid);
228/*        IDEBUG("syllable from %d, length %d, invalid=%s", sstart, send-sstart,
229                 invalid ? "TRUE" : "FALSE"); */
230        syllable.item.pos = sstart;
231        syllable.item.length = send-sstart;
232        syllable.glyphs = item->glyphs + first_glyph;
233        syllable.attributes = item->attributes + first_glyph;
234        syllable.offsets = item->offsets + first_glyph;
235        syllable.advances = item->advances + first_glyph;
236        syllable.num_glyphs = item->num_glyphs - first_glyph;
237        if (!tibetan_shape_syllable(openType, &syllable, invalid)) {
238            item->num_glyphs += syllable.num_glyphs;
239            return FALSE;
240        }
241        /* fix logcluster array */
242        for (i = sstart; i < send; ++i)
243            logClusters[i-item->item.pos] = first_glyph;
244        sstart = send;
245        first_glyph += syllable.num_glyphs;
246    }
247    item->num_glyphs = first_glyph;
248    return TRUE;
249}
250
251void HB_TibetanAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
252{
253    int end = from + len;
254    const HB_UChar16 *uc = text + from;
255    hb_uint32 i = 0;
256    HB_UNUSED(script);
257    attributes += from;
258    while (i < len) {
259        HB_Bool invalid;
260        hb_uint32 boundary = tibetan_nextSyllableBoundary(text, from+i, end, &invalid) - from;
261
262        attributes[i].charStop = TRUE;
263
264        if (boundary > len-1) boundary = len;
265        i++;
266        while (i < boundary) {
267            attributes[i].charStop = FALSE;
268            ++uc;
269            ++i;
270        }
271        assert(i == boundary);
272    }
273}
274
275
276