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