af0ee52e9a6b15d1cf53c8ff8eafee56c2e4be60
[profile/ivi/qtbase.git] / src / 3rdparty / harfbuzz / src / harfbuzz-shaper.cpp
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 "harfbuzz-stream-private.h"
29 #include <assert.h>
30 #include <stdio.h>
31
32 #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
33 #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
34
35 // -----------------------------------------------------------------------------------------------------
36 //
37 // The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
38 //
39 // -----------------------------------------------------------------------------------------------------
40
41 /* The Unicode algorithm does in our opinion allow line breaks at some
42    places they shouldn't be allowed. The following changes were thus
43    made in comparison to the Unicode reference:
44
45    EX->AL from DB to IB
46    SY->AL from DB to IB
47    SY->PO from DB to IB
48    SY->PR from DB to IB
49    SY->OP from DB to IB
50    AL->PR from DB to IB
51    AL->PO from DB to IB
52    PR->PR from DB to IB
53    PO->PO from DB to IB
54    PR->PO from DB to IB
55    PO->PR from DB to IB
56    HY->PO from DB to IB
57    HY->PR from DB to IB
58    HY->OP from DB to IB
59    NU->EX from PB to IB
60    EX->PO from DB to IB
61 */
62
63 // The following line break classes are not treated by the table:
64 //  AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
65
66 enum break_class {
67     // the first 4 values have to agree with the enum in QCharAttributes
68     ProhibitedBreak,            // PB in table
69     DirectBreak,                // DB in table
70     IndirectBreak,              // IB in table
71     CombiningIndirectBreak,     // CI in table
72     CombiningProhibitedBreak    // CP in table
73 };
74 #define DB DirectBreak
75 #define IB IndirectBreak
76 #define CI CombiningIndirectBreak
77 #define CP CombiningProhibitedBreak
78 #define PB ProhibitedBreak
79
80 static const hb_uint8 breakTable[HB_LineBreak_JT+1][HB_LineBreak_JT+1] =
81 {
82 /*          OP  CL  QU  GL  NS  EX  SY  IS  PR  PO  NU  AL  ID  IN  HY  BA  BB  B2  ZW  CM  WJ  H2  H3  JL  JV  JT */
83 /* OP */ { PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, CP, PB, PB, PB, PB, PB, PB },
84 /* CL */ { DB, PB, IB, IB, PB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
85 /* QU */ { PB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
86 /* GL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
87 /* NS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
88 /* EX */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
89 /* SY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
90 /* IS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
91 /* PR */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, DB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, IB },
92 /* PO */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
93 /* NU */ { IB, PB, IB, IB, IB, IB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
94 /* AL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
95 /* ID */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
96 /* IN */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
97 /* HY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
98 /* BA */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
99 /* BB */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
100 /* B2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, PB, PB, CI, PB, DB, DB, DB, DB, DB },
101 /* ZW */ { DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, PB, DB, DB, DB, DB, DB, DB, DB },
102 /* CM */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
103 /* WJ */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
104 /* H2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB },
105 /* H3 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB },
106 /* JL */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, DB },
107 /* JV */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB },
108 /* JT */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB }
109 };
110 #undef DB
111 #undef IB
112 #undef CI
113 #undef CP
114 #undef PB
115
116 static const hb_uint8 graphemeTable[HB_Grapheme_LVT + 1][HB_Grapheme_LVT + 1] =
117 {
118 //      Other, CR,    LF,    Control,Extend,L,    V,     T,     LV,    LVT
119     { true , true , true , true , true , true , true , true , true , true  }, // Other, 
120     { true , true , true , true , true , true , true , true , true , true  }, // CR,
121     { true , false, true , true , true , true , true , true , true , true  }, // LF,
122     { true , true , true , true , true , true , true , true , true , true  }, // Control,
123     { false, true , true , true , false, false, false, false, false, false }, // Extend,
124     { true , true , true , true , true , false, true , true , true , true  }, // L, 
125     { true , true , true , true , true , false, false, true , false, true  }, // V, 
126     { true , true , true , true , true , true , false, false, false, false }, // T, 
127     { true , true , true , true , true , false, true , true , true , true  }, // LV, 
128     { true , true , true , true , true , false, true , true , true , true  }, // LVT
129 };
130     
131 static void calcLineBreaks(const HB_UChar16 *uc, hb_uint32 len, HB_CharAttributes *charAttributes)
132 {
133     if (!len)
134         return;
135
136     // ##### can this fail if the first char is a surrogate?
137     HB_LineBreakClass cls;
138     HB_GraphemeClass grapheme;
139     HB_GetGraphemeAndLineBreakClass(*uc, &grapheme, &cls);
140     // handle case where input starts with an LF
141     if (cls == HB_LineBreak_LF)
142         cls = HB_LineBreak_BK;
143
144     charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK);
145     charAttributes[0].charStop = true;
146
147     int lcls = cls;
148     for (hb_uint32 i = 1; i < len; ++i) {
149         charAttributes[i].whiteSpace = false;
150         charAttributes[i].charStop = true;
151
152         HB_UChar32 code = uc[i];
153         HB_GraphemeClass ngrapheme;
154         HB_LineBreakClass ncls;
155         HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
156         charAttributes[i].charStop = graphemeTable[ngrapheme][grapheme];
157         // handle surrogates
158         if (ncls == HB_LineBreak_SG) {
159             if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) {
160                 continue;
161             } else if (HB_IsLowSurrogate(uc[i]) && HB_IsHighSurrogate(uc[i-1])) {
162                 code = HB_SurrogateToUcs4(uc[i-1], uc[i]);
163                 HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
164                 charAttributes[i].charStop = false;
165             } else {
166                 ncls = HB_LineBreak_AL;
167             }
168         }
169
170         // set white space and char stop flag
171         if (ncls >= HB_LineBreak_SP)
172             charAttributes[i].whiteSpace = true;
173
174         HB_LineBreakType lineBreakType = HB_NoBreak;
175         if (cls >= HB_LineBreak_LF) {
176             lineBreakType = HB_ForcedBreak;
177         } else if(cls == HB_LineBreak_CR) {
178             lineBreakType = (ncls == HB_LineBreak_LF) ? HB_NoBreak : HB_ForcedBreak;
179         }
180
181         if (ncls == HB_LineBreak_SP)
182             goto next_no_cls_update;
183         if (ncls >= HB_LineBreak_CR)
184             goto next;
185
186         {
187             int tcls = ncls;
188             // for south east asian chars that require a complex (dictionary analysis), the unicode
189             // standard recommends to treat them as AL. thai_attributes and other attribute methods that
190             // do dictionary analysis can override
191             if (tcls >= HB_LineBreak_SA)
192                 tcls = HB_LineBreak_AL;
193             if (cls >= HB_LineBreak_SA)
194                 cls = HB_LineBreak_AL;
195
196             int brk = breakTable[cls][tcls];
197             switch (brk) {
198             case DirectBreak:
199                 lineBreakType = HB_Break;
200                 if (uc[i-1] == 0xad) // soft hyphen
201                     lineBreakType = HB_SoftHyphen;
202                 break;
203             case IndirectBreak:
204                 lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
205                 break;
206             case CombiningIndirectBreak:
207                 lineBreakType = HB_NoBreak;
208                 if (lcls == HB_LineBreak_SP){
209                     if (i > 1)
210                         charAttributes[i-2].lineBreakType = HB_Break;
211                 } else {
212                     goto next_no_cls_update;
213                 }
214                 break;
215             case CombiningProhibitedBreak:
216                 lineBreakType = HB_NoBreak;
217                 if (lcls != HB_LineBreak_SP)
218                     goto next_no_cls_update;
219             case ProhibitedBreak:
220             default:
221                 break;
222             }
223         }
224     next:
225         cls = ncls;
226     next_no_cls_update:
227         lcls = ncls;
228         grapheme = ngrapheme;
229         charAttributes[i-1].lineBreakType = lineBreakType;
230     }
231     charAttributes[len-1].lineBreakType = HB_ForcedBreak;
232 }
233
234 // --------------------------------------------------------------------------------------------------------------------------------------------
235 //
236 // Basic processing
237 //
238 // --------------------------------------------------------------------------------------------------------------------------------------------
239
240 static inline void positionCluster(HB_ShaperItem *item, int gfrom,  int glast)
241 {
242     int nmarks = glast - gfrom;
243     assert(nmarks > 0);
244
245     HB_Glyph *glyphs = item->glyphs;
246     HB_GlyphAttributes *attributes = item->attributes;
247
248     HB_GlyphMetrics baseMetrics;
249     item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
250
251     if (item->item.script == HB_Script_Hebrew
252         && (-baseMetrics.y) > baseMetrics.height)
253         // we need to attach below the baseline, because of the hebrew iud.
254         baseMetrics.height = -baseMetrics.y;
255
256 //     qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
257 //     qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
258
259     HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
260     HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
261     if (size > HB_FIXED_CONSTANT(4))
262         offsetBase += HB_FIXED_CONSTANT(4);
263     else
264         offsetBase += size;
265     //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
266 //     qDebug("offset = %f", offsetBase);
267
268     // To fix some Thai character heights check for two above glyphs
269     if (nmarks == 2 && (attributes[gfrom+1].combiningClass == HB_Combining_AboveRight ||
270             attributes[gfrom+1].combiningClass  == HB_Combining_AboveLeft ||
271             attributes[gfrom+1].combiningClass == HB_Combining_Above))
272         if (attributes[gfrom+2].combiningClass == 23 ||
273             attributes[gfrom+2].combiningClass == 24 ||
274             attributes[gfrom+2].combiningClass == 25 ||
275             attributes[gfrom+2].combiningClass == 27 ||
276             attributes[gfrom+2].combiningClass == 28 ||
277             attributes[gfrom+2].combiningClass == 30 ||
278             attributes[gfrom+2].combiningClass == 31 ||
279             attributes[gfrom+2].combiningClass == 33 ||
280             attributes[gfrom+2].combiningClass == 34 ||
281             attributes[gfrom+2].combiningClass == 35 ||
282             attributes[gfrom+2].combiningClass == 36 ||
283             attributes[gfrom+2].combiningClass == 107 ||
284             attributes[gfrom+2].combiningClass == 122) {
285             // Two above glyphs, check total height
286             int markTotalHeight = baseMetrics.height;
287             HB_GlyphMetrics markMetrics;
288             item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom+1], &markMetrics);
289             markTotalHeight += markMetrics.height;
290             item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom+2], &markMetrics);
291             markTotalHeight += markMetrics.height;
292             if ((markTotalHeight + 2 * offsetBase) > (size * 10))
293                 offsetBase = ((size * 10) - markTotalHeight) / 2; // Use offset that just fits
294         }
295
296     bool rightToLeft = item->item.bidiLevel % 2;
297
298     int i;
299     unsigned char lastCmb = 0;
300     HB_GlyphMetrics attachmentRect;
301     memset(&attachmentRect, 0, sizeof(attachmentRect));
302
303     for(i = 1; i <= nmarks; i++) {
304         HB_Glyph mark = glyphs[gfrom+i];
305         HB_GlyphMetrics markMetrics;
306         item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
307         HB_FixedPoint p;
308         p.x = p.y = 0;
309 //          qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
310
311         HB_Fixed offset = offsetBase;
312         unsigned char cmb = attributes[gfrom+i].combiningClass;
313
314         // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
315         // bits  in the glyphAttributes structure.
316         if (cmb < 200) {
317             // fixed position classes. We approximate by mapping to one of the others.
318             // currently I added only the ones for arabic, hebrew, lao and thai.
319
320             // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
321
322             // add a bit more offset to arabic, a bit hacky
323             if (cmb >= 27 && cmb <= 36 && offset < 3)
324                 offset +=1;
325             // below
326             if ((cmb >= 10 && cmb <= 18) ||
327                  cmb == 20 || cmb == 22 ||
328                  cmb == 29 || cmb == 32)
329                 cmb = HB_Combining_Below;
330             // above
331             else if (cmb == 23 || cmb == 27 || cmb == 28 ||
332                       cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
333                 cmb = HB_Combining_Above;
334             //below-right
335             else if (cmb == 9 || cmb == 103 || cmb == 118)
336                 cmb = HB_Combining_BelowRight;
337             // above-right
338             else if (cmb == 24 || cmb == 107 || cmb == 122)
339                 cmb = HB_Combining_AboveRight;
340             else if (cmb == 25)
341                 cmb = HB_Combining_AboveLeft;
342             // fixed:
343             //  19 21
344
345         }
346
347         // combining marks of different class don't interact. Reset the rectangle.
348         if (cmb != lastCmb) {
349             //qDebug("resetting rect");
350             attachmentRect = baseMetrics;
351         }
352
353         switch(cmb) {
354         case HB_Combining_DoubleBelow:
355                 // ### wrong in rtl context!
356         case HB_Combining_BelowLeft:
357             p.y += offset;
358         case HB_Combining_BelowLeftAttached:
359             p.x += attachmentRect.x - markMetrics.x;
360             p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
361             break;
362         case HB_Combining_Below:
363             p.y += offset;
364         case HB_Combining_BelowAttached:
365             p.x += attachmentRect.x - markMetrics.x;
366             p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
367
368             p.x += (attachmentRect.width - markMetrics.width) / 2;
369             break;
370         case HB_Combining_BelowRight:
371             p.y += offset;
372         case HB_Combining_BelowRightAttached:
373             p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
374             p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
375             break;
376         case HB_Combining_Left:
377             p.x -= offset;
378         case HB_Combining_LeftAttached:
379             break;
380         case HB_Combining_Right:
381             p.x += offset;
382         case HB_Combining_RightAttached:
383             break;
384         case HB_Combining_DoubleAbove:
385             // ### wrong in RTL context!
386         case HB_Combining_AboveLeft:
387             p.y -= offset;
388         case HB_Combining_AboveLeftAttached:
389             p.x += attachmentRect.x - markMetrics.x;
390             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
391             break;
392         case HB_Combining_Above:
393             p.y -= offset;
394         case HB_Combining_AboveAttached:
395             p.x += attachmentRect.x - markMetrics.x;
396             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
397
398             p.x += (attachmentRect.width - markMetrics.width) / 2;
399             break;
400         case HB_Combining_AboveRight:
401             p.y -= offset;
402         case HB_Combining_AboveRightAttached:
403             p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
404             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
405             break;
406
407         case HB_Combining_IotaSubscript:
408             default:
409                 break;
410         }
411 //          qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
412         markMetrics.x += p.x;
413         markMetrics.y += p.y;
414
415         HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
416         unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
417         unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
418         unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
419         unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
420         attachmentRect = unitedAttachmentRect;
421
422         lastCmb = cmb;
423         if (rightToLeft) {
424             item->offsets[gfrom+i].x = p.x;
425             item->offsets[gfrom+i].y = p.y;
426         } else {
427             item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
428             item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
429         }
430         item->advances[gfrom+i] = 0;
431     }
432 }
433
434 void HB_HeuristicPosition(HB_ShaperItem *item)
435 {
436     HB_GetGlyphAdvances(item);
437     HB_GlyphAttributes *attributes = item->attributes;
438
439     int cEnd = -1;
440     int i = item->num_glyphs;
441     while (i--) {
442         if (cEnd == -1 && attributes[i].mark) {
443             cEnd = i;
444         } else if (cEnd != -1 && !attributes[i].mark) {
445             positionCluster(item, i, cEnd);
446             cEnd = -1;
447         }
448     }
449 }
450
451 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
452 // and no reordering.
453 // also computes logClusters heuristically
454 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
455 {
456     const HB_UChar16 *uc = item->string + item->item.pos;
457     hb_uint32 length = item->item.length;
458
459     // ### zeroWidth and justification are missing here!!!!!
460
461     assert(item->num_glyphs <= length);
462
463 //     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
464     HB_GlyphAttributes *attributes = item->attributes;
465     unsigned short *logClusters = item->log_clusters;
466
467     hb_uint32 glyph_pos = 0;
468     hb_uint32 i;
469     for (i = 0; i < length; i++) {
470         if (HB_IsHighSurrogate(uc[i]) && i < length - 1
471             && HB_IsLowSurrogate(uc[i + 1])) {
472             logClusters[i] = glyph_pos;
473             logClusters[++i] = glyph_pos;
474         } else {
475             logClusters[i] = glyph_pos;
476         }
477         ++glyph_pos;
478     }
479     assert(glyph_pos == item->num_glyphs);
480
481     // first char in a run is never (treated as) a mark
482     int cStart = 0;
483     const bool symbolFont = item->face->isSymbolFont;
484     attributes[0].mark = false;
485     attributes[0].clusterStart = true;
486     attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
487
488     int pos = 0;
489     HB_CharCategory lastCat;
490     int dummy;
491     HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
492     for (i = 1; i < length; ++i) {
493         if (logClusters[i] == pos)
494             // same glyph
495             continue;
496         ++pos;
497         while (pos < logClusters[i]) {
498             attributes[pos] = attributes[pos-1];
499             ++pos;
500         }
501         // hide soft-hyphens by default
502         if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
503             attributes[pos].dontPrint = true;
504         HB_CharCategory cat;
505         int cmb;
506         HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
507         if (cat != HB_Mark_NonSpacing) {
508             attributes[pos].mark = false;
509             attributes[pos].clusterStart = true;
510             attributes[pos].combiningClass = 0;
511             cStart = logClusters[i];
512         } else {
513             if (cmb == 0) {
514                 // Fix 0 combining classes
515                 if ((uc[pos] & 0xff00) == 0x0e00) {
516                     // thai or lao
517                     if (uc[pos] == 0xe31 ||
518                          uc[pos] == 0xe34 ||
519                          uc[pos] == 0xe35 ||
520                          uc[pos] == 0xe36 ||
521                          uc[pos] == 0xe37 ||
522                          uc[pos] == 0xe47 ||
523                          uc[pos] == 0xe4c ||
524                          uc[pos] == 0xe4d ||
525                          uc[pos] == 0xe4e) {
526                         cmb = HB_Combining_AboveRight;
527                     } else if (uc[pos] == 0xeb1 ||
528                                 uc[pos] == 0xeb4 ||
529                                 uc[pos] == 0xeb5 ||
530                                 uc[pos] == 0xeb6 ||
531                                 uc[pos] == 0xeb7 ||
532                                 uc[pos] == 0xebb ||
533                                 uc[pos] == 0xecc ||
534                                 uc[pos] == 0xecd) {
535                         cmb = HB_Combining_Above;
536                     } else if (uc[pos] == 0xebc) {
537                         cmb = HB_Combining_Below;
538                     }
539                 }
540             }
541
542             attributes[pos].mark = true;
543             attributes[pos].clusterStart = false;
544             attributes[pos].combiningClass = cmb;
545             logClusters[i] = cStart;
546         }
547         // one gets an inter character justification point if the current char is not a non spacing mark.
548         // as then the current char belongs to the last one and one gets a space justification point
549         // after the space char.
550         if (lastCat == HB_Separator_Space)
551             attributes[pos-1].justification = HB_Space;
552         else if (cat != HB_Mark_NonSpacing)
553             attributes[pos-1].justification = HB_Character;
554         else
555             attributes[pos-1].justification = HB_NoJustification;
556
557         lastCat = cat;
558     }
559     pos = logClusters[length-1];
560     if (lastCat == HB_Separator_Space)
561         attributes[pos].justification = HB_Space;
562     else
563         attributes[pos].justification = HB_Character;
564 }
565
566 #ifndef NO_OPENTYPE
567 static const HB_OpenTypeFeature basic_features[] = {
568     { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
569     { HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
570     { HB_MAKE_TAG('c', 'l', 'i', 'g'), CligProperty },
571     {0, 0}
572 };
573
574 static const HB_OpenTypeFeature disabled_features[] = {
575     { HB_MAKE_TAG('c', 'p', 'c', 't'), PositioningProperties },
576     { HB_MAKE_TAG('h', 'a', 'l', 't'), PositioningProperties },
577     // TODO: we need to add certain HB_ShaperFlag for vertical
578     // writing mode to enable these vertical writing features:
579     { HB_MAKE_TAG('v', 'a', 'l', 't'), PositioningProperties },
580     { HB_MAKE_TAG('v', 'h', 'a', 'l'), PositioningProperties },
581     { HB_MAKE_TAG('v', 'k', 'r', 'n'), PositioningProperties },
582     { HB_MAKE_TAG('v', 'p', 'a', 'l'), PositioningProperties },
583     {0, 0}
584 };
585 #endif
586
587 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
588 {
589     if (shaper_item->glyphIndicesPresent) {
590         shaper_item->num_glyphs = shaper_item->initialGlyphCount;
591         shaper_item->glyphIndicesPresent = false;
592         return true;
593     }
594     return shaper_item->font->klass
595            ->convertStringToGlyphIndices(shaper_item->font,
596                                          shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
597                                          shaper_item->glyphs, &shaper_item->num_glyphs,
598                                          shaper_item->item.bidiLevel % 2);
599 }
600
601 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
602 {
603 #ifndef NO_OPENTYPE
604     const int availableGlyphs = shaper_item->num_glyphs;
605 #endif
606
607     if (!HB_ConvertStringToGlyphIndices(shaper_item))
608         return false;
609
610     HB_HeuristicSetGlyphAttributes(shaper_item);
611
612 #ifndef NO_OPENTYPE
613     if (HB_SelectScript(shaper_item, basic_features)) {
614         HB_OpenTypeShape(shaper_item, /*properties*/0);
615         return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
616     }
617 #endif
618
619     HB_HeuristicPosition(shaper_item);
620     return true;
621 }
622
623 const HB_ScriptEngine HB_ScriptEngines[] = {
624     // Common
625     { HB_BasicShape, 0},
626     // Greek
627     { HB_GreekShape, 0},
628     // Cyrillic
629     { HB_BasicShape, 0},
630     // Armenian
631     { HB_BasicShape, 0},
632     // Hebrew
633     { HB_HebrewShape, 0 },
634     // Arabic
635     { HB_ArabicShape, 0},
636     // Syriac
637     { HB_ArabicShape, 0},
638     // Thaana
639     { HB_BasicShape, 0 },
640     // Devanagari
641     { HB_IndicShape, HB_IndicAttributes },
642     // Bengali
643     { HB_IndicShape, HB_IndicAttributes },
644     // Gurmukhi
645     { HB_IndicShape, HB_IndicAttributes },
646     // Gujarati
647     { HB_IndicShape, HB_IndicAttributes },
648     // Oriya
649     { HB_IndicShape, HB_IndicAttributes },
650     // Tamil
651     { HB_IndicShape, HB_IndicAttributes },
652     // Telugu
653     { HB_IndicShape, HB_IndicAttributes },
654     // Kannada
655     { HB_IndicShape, HB_IndicAttributes },
656     // Malayalam
657     { HB_IndicShape, HB_IndicAttributes },
658     // Sinhala
659     { HB_IndicShape, HB_IndicAttributes },
660     // Thai
661     { HB_ThaiShape, HB_ThaiAttributes },
662     // Lao
663     { HB_BasicShape, 0 },
664     // Tibetan
665     { HB_TibetanShape, HB_TibetanAttributes },
666     // Myanmar
667     { HB_MyanmarShape, HB_MyanmarAttributes },
668     // Georgian
669     { HB_BasicShape, 0 },
670     // Hangul
671     { HB_HangulShape, 0 },
672     // Ogham
673     { HB_BasicShape, 0 },
674     // Runic
675     { HB_BasicShape, 0 },
676     // Khmer
677     { HB_KhmerShape, HB_KhmerAttributes },
678     // N'Ko
679     { HB_ArabicShape, 0}
680 };
681
682 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
683                           const HB_ScriptItem *items, hb_uint32 numItems,
684                           HB_CharAttributes *attributes)
685 {
686     memset(attributes, 0, stringLength * sizeof(HB_CharAttributes));
687     calcLineBreaks(string, stringLength, attributes);
688
689     for (hb_uint32 i = 0; i < numItems; ++i) {
690         HB_Script script = items[i].script;
691         if (script == HB_Script_Inherited)
692             script = HB_Script_Common;
693         HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
694         if (!attributeFunction)
695             continue;
696         attributeFunction(script, string, items[i].pos, items[i].length, attributes);
697     }
698 }
699
700
701 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
702
703 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
704 //        Other    Format   Katakana ALetter  MidLetter MidNum  Numeric  ExtendNumLet
705     {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // Other
706     {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // Format 
707     {   Break,   Break, NoBreak,   Break,   Break,   Break,   Break, NoBreak }, // Katakana
708     {   Break,   Break,   Break, NoBreak,  Middle,   Break, NoBreak, NoBreak }, // ALetter
709     {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // MidLetter
710     {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // MidNum
711     {   Break,   Break,   Break, NoBreak,   Break,  Middle, NoBreak, NoBreak }, // Numeric
712     {   Break,   Break, NoBreak, NoBreak,   Break,   Break, NoBreak, NoBreak }, // ExtendNumLet
713 };
714
715 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
716                           const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
717                           HB_CharAttributes *attributes)
718 {
719     if (stringLength == 0)
720         return;
721     unsigned int brk = HB_GetWordClass(string[0]);
722     attributes[0].wordBoundary = true;
723     for (hb_uint32 i = 1; i < stringLength; ++i) {
724         if (!attributes[i].charStop) {
725             attributes[i].wordBoundary = false;
726             continue;
727         }
728         hb_uint32 nbrk = HB_GetWordClass(string[i]);
729         if (nbrk == HB_Word_Format) {
730             attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
731             continue;
732         }
733         BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
734         if (rule == Middle) {
735             rule = Break;
736             hb_uint32 lookahead = i + 1;
737             while (lookahead < stringLength) {
738                 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
739                 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
740                     ++lookahead;
741                     continue;
742                 }
743                 if (testbrk == brk) {
744                     rule = NoBreak;
745                     while (i < lookahead)
746                         attributes[i++].wordBoundary = false;
747                     nbrk = testbrk;
748                 }
749                 break;
750             }
751         }
752         attributes[i].wordBoundary = (rule == Break);
753         brk = nbrk;
754     }
755 }
756
757
758 enum SentenceBreakStates {
759     SB_Initial,
760     SB_Upper,
761     SB_UpATerm, 
762     SB_ATerm,
763     SB_ATermC, 
764     SB_ACS, 
765     SB_STerm, 
766     SB_STermC, 
767     SB_SCS,
768     SB_BAfter, 
769     SB_Break,
770     SB_Look
771 };
772
773 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
774 //        Other       Sep         Format      Sp          Lower       Upper       OLetter     Numeric     ATerm       STerm       Close
775       { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper  , SB_Initial, SB_Initial, SB_ATerm  , SB_STerm  , SB_Initial }, // SB_Initial,
776       { SB_Initial, SB_BAfter , SB_Upper  , SB_Initial, SB_Initial, SB_Upper  , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm  , SB_Initial }, // SB_Upper
777       
778       { SB_Look   , SB_BAfter , SB_UpATerm, SB_ACS    , SB_Initial, SB_Upper  , SB_Break  , SB_Initial, SB_ATerm  , SB_STerm  , SB_ATermC  }, // SB_UpATerm
779       { SB_Look   , SB_BAfter , SB_ATerm  , SB_ACS    , SB_Initial, SB_Break  , SB_Break  , SB_Initial, SB_ATerm  , SB_STerm  , SB_ATermC  }, // SB_ATerm
780       { SB_Look   , SB_BAfter , SB_ATermC , SB_ACS    , SB_Initial, SB_Break  , SB_Break  , SB_Look   , SB_ATerm  , SB_STerm  , SB_ATermC  }, // SB_ATermC,
781       { SB_Look   , SB_BAfter , SB_ACS    , SB_ACS    , SB_Initial, SB_Break  , SB_Break  , SB_Look   , SB_ATerm  , SB_STerm  , SB_Look    }, // SB_ACS,
782       
783       { SB_Break  , SB_BAfter , SB_STerm  , SB_SCS    , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_ATerm  , SB_STerm  , SB_STermC  }, // SB_STerm,
784       { SB_Break  , SB_BAfter , SB_STermC , SB_SCS    , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_ATerm  , SB_STerm  , SB_STermC  }, // SB_STermC,
785       { SB_Break  , SB_BAfter , SB_SCS    , SB_SCS    , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_ATerm  , SB_STerm  , SB_Break   }, // SB_SCS,
786       { SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break   }, // SB_BAfter,
787 };
788
789 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
790                               const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
791                               HB_CharAttributes *attributes)
792 {
793     if (stringLength == 0)
794         return;
795     hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
796     attributes[0].sentenceBoundary = true;
797     for (hb_uint32 i = 1; i < stringLength; ++i) {
798         if (!attributes[i].charStop) {
799             attributes[i].sentenceBoundary = false;
800             continue;
801         }
802         brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
803         if (brk == SB_Look) {
804             brk = SB_Break;
805             hb_uint32 lookahead = i + 1;
806             while (lookahead < stringLength) {
807                 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
808                 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
809                     break;
810                 } else if (sbrk == HB_Sentence_Lower) {
811                     brk = SB_Initial;
812                     break;
813                 }
814                 ++lookahead;
815             }
816             if (brk == SB_Initial) {
817                 while (i < lookahead)
818                     attributes[i++].sentenceBoundary = false;
819             }
820         }
821         if (brk == SB_Break) {
822             attributes[i].sentenceBoundary = true;
823             brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
824         } else {
825             attributes[i].sentenceBoundary = false;
826         }
827     }
828 }
829
830
831 static inline char *tag_to_string(HB_UInt tag)
832 {
833     static char string[5];
834     string[0] = (tag >> 24)&0xff;
835     string[1] = (tag >> 16)&0xff;
836     string[2] = (tag >> 8)&0xff;
837     string[3] = tag&0xff;
838     string[4] = 0;
839     return string;
840 }
841
842 #ifdef OT_DEBUG
843 static void dump_string(HB_Buffer buffer)
844 {
845     for (uint i = 0; i < buffer->in_length; ++i) {
846         qDebug("    %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
847     }
848 }
849 #define DEBUG printf
850 #else
851 #define DEBUG if (1) ; else printf
852 #endif
853
854 #define DefaultLangSys 0xffff
855 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
856
857 enum {
858     RequiresGsub = 1,
859     RequiresGpos = 2
860 };
861
862 struct OTScripts {
863     unsigned int tag;
864     int flags;
865 };
866 static const OTScripts ot_scripts [] = {
867     // Common
868     { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
869     // Greek
870     { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
871     // Cyrillic
872     { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
873     // Armenian
874     { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
875     // Hebrew
876     { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
877     // Arabic
878     { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
879     // Syriac
880     { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
881     // Thaana
882     { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
883     // Devanagari
884     { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
885     // Bengali
886     { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
887     // Gurmukhi
888     { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
889     // Gujarati
890     { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
891     // Oriya
892     { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
893     // Tamil
894     { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
895     // Telugu
896     { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
897     // Kannada
898     { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
899     // Malayalam
900     { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
901     // Sinhala
902     { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
903     // Thai
904     { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
905     // Lao
906     { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
907     // Tibetan
908     { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
909     // Myanmar
910     { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
911     // Georgian
912     { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
913     // Hangul
914     { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
915     // Ogham
916     { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
917     // Runic
918     { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
919     // Khmer
920     { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
921     // N'Ko
922     { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
923 };
924 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
925
926 static HB_Bool checkScript(HB_Face face, int script)
927 {
928     assert(script < HB_ScriptCount);
929
930     if (!face->gsub && !face->gpos)
931         return false;
932
933     unsigned int tag = ot_scripts[script].tag;
934     int requirements = ot_scripts[script].flags;
935
936     if (requirements & RequiresGsub) {
937         if (!face->gsub)
938             return false;
939
940         HB_UShort script_index;
941         HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
942         if (error) {
943             DEBUG("could not select script %d in GSub table: %d", (int)script, error);
944             error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
945             if (error)
946                 return false;
947         }
948     }
949
950     if (requirements & RequiresGpos) {
951         if (!face->gpos)
952             return false;
953
954         HB_UShort script_index;
955         HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
956         if (error) {
957             DEBUG("could not select script in gpos table: %d", error);
958             error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
959             if (error)
960                 return false;
961         }
962
963     }
964     return true;
965 }
966
967 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
968 {
969     HB_Error error;
970     HB_UInt length = 0;
971     HB_Stream stream = 0;
972
973     if (!font)
974         return 0;
975
976     error = tableFunc(font, tag, 0, &length);
977     if (error)
978         return 0;
979     stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
980     if (!stream)
981         return 0;
982     stream->base = (HB_Byte*)malloc(length);
983     if (!stream->base) {
984         free(stream);
985         return 0;
986     }
987     error = tableFunc(font, tag, stream->base, &length);
988     if (error) {
989         _hb_close_stream(stream);
990         return 0;
991     }
992     stream->size = length;
993     stream->pos = 0;
994     stream->cursor = NULL;
995     return stream;
996 }
997
998 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
999 {
1000     HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
1001     if (!face)
1002         return 0;
1003
1004     face->isSymbolFont = false;
1005     face->gdef = 0;
1006     face->gpos = 0;
1007     face->gsub = 0;
1008     face->current_script = HB_ScriptCount;
1009     face->current_flags = HB_ShaperFlag_Default;
1010     face->has_opentype_kerning = false;
1011     face->tmpAttributes = 0;
1012     face->tmpLogClusters = 0;
1013     face->glyphs_substituted = false;
1014     face->buffer = 0;
1015
1016     HB_Error error = HB_Err_Ok;
1017     HB_Stream stream;
1018     HB_Stream gdefStream;
1019
1020     gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
1021     error = HB_Err_Not_Covered;
1022     if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
1023         //DEBUG("error loading gdef table: %d", error);
1024         face->gdef = 0;
1025     }
1026
1027     //DEBUG() << "trying to load gsub table";
1028     stream = getTableStream(font, tableFunc, TTAG_GSUB);
1029     error = HB_Err_Not_Covered;
1030     if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
1031         face->gsub = 0;
1032         if (error != HB_Err_Not_Covered) {
1033             //DEBUG("error loading gsub table: %d", error);
1034         } else {
1035             //DEBUG("face doesn't have a gsub table");
1036         }
1037     }
1038     _hb_close_stream(stream);
1039
1040     stream = getTableStream(font, tableFunc, TTAG_GPOS);
1041     error = HB_Err_Not_Covered;
1042     if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1043         face->gpos = 0;
1044         DEBUG("error loading gpos table: %d", error);
1045     }
1046     _hb_close_stream(stream);
1047
1048     _hb_close_stream(gdefStream);
1049
1050     for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1051         face->supported_scripts[i] = checkScript(face, i);
1052
1053     if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1054         HB_FreeFace(face);
1055         return 0;
1056     }
1057
1058     return face;
1059 }
1060
1061 void HB_FreeFace(HB_Face face)
1062 {
1063     if (!face)
1064         return;
1065     if (face->gpos)
1066         HB_Done_GPOS_Table(face->gpos);
1067     if (face->gsub)
1068         HB_Done_GSUB_Table(face->gsub);
1069     if (face->gdef)
1070         HB_Done_GDEF_Table(face->gdef);
1071     if (face->buffer)
1072         hb_buffer_free(face->buffer);
1073     if (face->tmpAttributes)
1074         free(face->tmpAttributes);
1075     if (face->tmpLogClusters)
1076         free(face->tmpLogClusters);
1077     free(face);
1078 }
1079
1080 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1081 {
1082     HB_Script script = shaper_item->item.script;
1083
1084     HB_Face face = shaper_item->face;
1085     if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1086         return shaper_item->face->supported_scripts[script] ? true : false;
1087
1088     face->current_script = script;
1089     face->current_flags = shaper_item->shaperFlags;
1090
1091     if (!shaper_item->face->supported_scripts[script])
1092         return false;
1093
1094     assert(script < HB_ScriptCount);
1095     // find script in our list of supported scripts.
1096     unsigned int tag = ot_scripts[script].tag;
1097
1098     if (face->gsub && features) {
1099 #ifdef OT_DEBUG
1100         {
1101             HB_FeatureList featurelist = face->gsub->FeatureList;
1102             int numfeatures = featurelist.FeatureCount;
1103             DEBUG("gsub table has %d features", numfeatures);
1104             for (int i = 0; i < numfeatures; i++) {
1105                 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1106                 DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
1107             }
1108         }
1109 #endif
1110         HB_GSUB_Clear_Features(face->gsub);
1111         HB_UShort script_index;
1112         HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1113         if (!error) {
1114             DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1115             while (features->tag) {
1116                 HB_UShort feature_index;
1117                 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1118                 if (!error) {
1119                     DEBUG("  adding feature %s", tag_to_string(features->tag));
1120                     HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1121                 }
1122                 ++features;
1123             }
1124         }
1125     }
1126
1127     // reset
1128     face->has_opentype_kerning = false;
1129
1130     if (face->gpos) {
1131         HB_GPOS_Clear_Features(face->gpos);
1132         HB_UShort script_index;
1133         HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1134         if (!error) {
1135 #ifdef OT_DEBUG
1136             {
1137                 HB_FeatureList featurelist = face->gpos->FeatureList;
1138                 int numfeatures = featurelist.FeatureCount;
1139                 DEBUG("gpos table has %d features", numfeatures);
1140                 for(int i = 0; i < numfeatures; i++) {
1141                     HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1142                     HB_UShort feature_index;
1143                     HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1144                     DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
1145                 }
1146             }
1147 #endif
1148             HB_UInt *feature_tag_list_buffer;
1149             error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1150             if (!error) {
1151                 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1152                 while (*feature_tag_list) {
1153                     HB_UShort feature_index;
1154                     bool skip = false;
1155                     if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1156                         if (face->current_flags & HB_ShaperFlag_NoKerning)
1157                             skip = true;
1158                         else
1159                             face->has_opentype_kerning = true;
1160                     }
1161                     features = disabled_features;
1162                     while (features->tag) {
1163                         if (*feature_tag_list == features->tag) {
1164                             skip = true;
1165                             break;
1166                         }
1167                         ++features;
1168                     }
1169                     // 'palt' should be turned off by default unless 'kern' is on
1170                     if (!face->has_opentype_kerning &&
1171                         *feature_tag_list == HB_MAKE_TAG('p', 'a', 'l', 't'))
1172                         skip = true;
1173
1174                     if (skip) {
1175                         ++feature_tag_list;
1176                         continue;
1177                     }
1178                     error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1179                     if (!error)
1180                         HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1181                     ++feature_tag_list;
1182                 }
1183                 FREE(feature_tag_list_buffer);
1184             }
1185         }
1186     }
1187
1188     return true;
1189 }
1190
1191 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1192 {
1193     HB_GlyphAttributes *tmpAttributes;
1194     unsigned int *tmpLogClusters;
1195
1196     HB_Face face = item->face;
1197
1198     face->length = item->num_glyphs;
1199
1200     hb_buffer_clear(face->buffer);
1201
1202     tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1203     if (!tmpAttributes)
1204         return false;
1205     face->tmpAttributes = tmpAttributes;
1206
1207     tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1208     if (!tmpLogClusters)
1209         return false;
1210     face->tmpLogClusters = tmpLogClusters;
1211
1212     for (int i = 0; i < face->length; ++i) {
1213         hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1214         face->tmpAttributes[i] = item->attributes[i];
1215         face->tmpLogClusters[i] = item->log_clusters[i];
1216     }
1217
1218 #ifdef OT_DEBUG
1219     DEBUG("-----------------------------------------");
1220 //     DEBUG("log clusters before shaping:");
1221 //     for (int j = 0; j < length; j++)
1222 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
1223     DEBUG("original glyphs: %p", item->glyphs);
1224     for (int i = 0; i < length; ++i)
1225         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
1226 //     dump_string(hb_buffer);
1227 #endif
1228
1229     face->glyphs_substituted = false;
1230     if (face->gsub) {
1231         unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1232         if (error && error != HB_Err_Not_Covered)
1233             return false;
1234         face->glyphs_substituted = (error != HB_Err_Not_Covered);
1235     }
1236
1237 #ifdef OT_DEBUG
1238 //     DEBUG("log clusters before shaping:");
1239 //     for (int j = 0; j < length; j++)
1240 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
1241     DEBUG("shaped glyphs:");
1242     for (int i = 0; i < length; ++i)
1243         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
1244     DEBUG("-----------------------------------------");
1245 //     dump_string(hb_buffer);
1246 #endif
1247
1248     return true;
1249 }
1250
1251 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1252 {
1253     HB_Face face = item->face;
1254
1255     bool glyphs_positioned = false;
1256     if (face->gpos) {
1257         if (face->buffer->positions)
1258             memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1259         // #### check that passing "false,false" is correct
1260         glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1261     }
1262
1263     if (!face->glyphs_substituted && !glyphs_positioned) {
1264         HB_HeuristicPosition(item);
1265         return true; // nothing to do for us
1266     }
1267
1268     // make sure we have enough space to write everything back
1269     if (availableGlyphs < (int)face->buffer->in_length) {
1270         item->num_glyphs = face->buffer->in_length;
1271         return false;
1272     }
1273
1274     HB_Glyph *glyphs = item->glyphs;
1275     HB_GlyphAttributes *attributes = item->attributes;
1276
1277     for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1278         glyphs[i] = face->buffer->in_string[i].gindex;
1279         attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
1280         if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
1281             attributes[i].clusterStart = false;
1282     }
1283     item->num_glyphs = face->buffer->in_length;
1284
1285     if (doLogClusters && face->glyphs_substituted) {
1286         // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1287         unsigned short *logClusters = item->log_clusters;
1288         int clusterStart = 0;
1289         int oldCi = 0;
1290         // #### the reconstruction of the logclusters currently does not work if the original string
1291         // contains surrogate pairs
1292         for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1293             int ci = face->buffer->in_string[i].cluster;
1294             //         DEBUG("   ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1295             //                i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1296             if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1297                 for (int j = oldCi; j < ci; j++)
1298                     logClusters[j] = clusterStart;
1299                 clusterStart = i;
1300                 oldCi = ci;
1301             }
1302         }
1303         for (int j = oldCi; j < face->length; j++)
1304             logClusters[j] = clusterStart;
1305     }
1306
1307     // calulate the advances for the shaped glyphs
1308 //     DEBUG("unpositioned: ");
1309
1310     // positioning code:
1311     if (glyphs_positioned) {
1312         HB_GetGlyphAdvances(item);
1313         HB_Position positions = face->buffer->positions;
1314         HB_Fixed *advances = item->advances;
1315
1316 //         DEBUG("positioned glyphs:");
1317         for (unsigned int i = 0; i < face->buffer->in_length; i++) {
1318 //             DEBUG("    %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1319 //                    glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1320 //                    (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1321 //                    (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1322 //                    positions[i].back, positions[i].new_advance);
1323
1324             HB_Fixed adjustment = positions[i].x_advance;
1325
1326             if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1327                 adjustment = HB_FIXED_ROUND(adjustment);
1328
1329             if (positions[i].new_advance) {
1330                 advances[i] = adjustment;
1331             } else {
1332                 advances[i] += adjustment;
1333             }
1334
1335             int back = 0;
1336             HB_FixedPoint *offsets = item->offsets;
1337             offsets[i].x = positions[i].x_pos;
1338             offsets[i].y = positions[i].y_pos;
1339             while (positions[i - back].back) {
1340                 back += positions[i - back].back;
1341                 offsets[i].x += positions[i - back].x_pos;
1342                 offsets[i].y += positions[i - back].y_pos;
1343             }
1344             offsets[i].y = -offsets[i].y;
1345
1346             if (item->item.bidiLevel % 2) {
1347                 // ### may need to go back multiple glyphs like in ltr
1348                 back = positions[i].back;
1349                 while (back--)
1350                     offsets[i].x -= advances[i-back];
1351             } else {
1352                 back = 0;
1353                 while (positions[i - back].back) {
1354                     back += positions[i - back].back;
1355                     offsets[i].x -= advances[i-back];
1356                 }
1357             }
1358 //             DEBUG("   ->\tadv=%d\tpos=(%d/%d)",
1359 //                    glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1360         }
1361         item->kerning_applied = face->has_opentype_kerning;
1362     } else {
1363         HB_HeuristicPosition(item);
1364     }
1365
1366 #ifdef OT_DEBUG
1367     if (doLogClusters) {
1368         DEBUG("log clusters after shaping:\n");
1369         for (unsigned int j = 0; j < item->item.length; j++)
1370             DEBUG("    log[%d] = %d\n", j, item->log_clusters[j]);
1371     }
1372     DEBUG("final glyphs:\n");
1373     for (unsigned int i = 0; i < item->num_glyphs; ++i)
1374         DEBUG("   glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d offset=%d/%d\n",
1375                glyphs[i], face->buffer->in_string[i].cluster, attributes[i].mark,
1376                attributes[i].combiningClass, attributes[i].clusterStart,
1377                item->advances[i] >> 6,
1378                item->offsets[i].x >> 6, item->offsets[i].y >> 6);
1379     DEBUG("-----------------------------------------\n");
1380 #endif
1381     return true;
1382 }
1383
1384 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1385 {
1386     HB_Bool result = false;
1387     if (shaper_item->num_glyphs < shaper_item->item.length) {
1388         shaper_item->num_glyphs = shaper_item->item.length;
1389         return false;
1390     }
1391     assert(shaper_item->item.script < HB_ScriptCount);
1392     result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1393     shaper_item->glyphIndicesPresent = false;
1394     return result;
1395 }
1396