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