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