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