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