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) {
   1086             DEBUG("script %s has script index %d", tag_to_string(script), script_index);
   1087             while (features->tag) {
   1088                 HB_UShort feature_index;
   1089                 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
   1090                 if (!error) {
   1091                     DEBUG("  adding feature %s", tag_to_string(features->tag));
   1092                     HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
   1093                 }
   1094                 ++features;
   1095             }
   1096         }
   1097     }
   1098 
   1099     // reset
   1100     face->has_opentype_kerning = false;
   1101 
   1102     if (face->gpos) {
   1103         HB_GPOS_Clear_Features(face->gpos);
   1104         HB_UShort script_index;
   1105         HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
   1106         if (!error) {
   1107 #ifdef OT_DEBUG
   1108             {
   1109                 HB_FeatureList featurelist = face->gpos->FeatureList;
   1110                 int numfeatures = featurelist.FeatureCount;
   1111                 DEBUG("gpos table has %d features", numfeatures);
   1112                 for(int i = 0; i < numfeatures; i++) {
   1113                     HB_FeatureRecord *r = featurelist.FeatureRecord + i;
   1114                     HB_UShort feature_index;
   1115                     HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
   1116                     DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
   1117                 }
   1118             }
   1119 #endif
   1120             HB_UInt *feature_tag_list_buffer;
   1121             error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
   1122             if (!error) {
   1123                 HB_UInt *feature_tag_list = feature_tag_list_buffer;
   1124                 while (*feature_tag_list) {
   1125                     HB_UShort feature_index;
   1126                     if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
   1127                         if (face->current_flags & HB_ShaperFlag_NoKerning) {
   1128                             ++feature_tag_list;
   1129                             continue;
   1130                         }
   1131                         face->has_opentype_kerning = true;
   1132                     }
   1133                     error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
   1134                     if (!error)
   1135                         HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
   1136                     ++feature_tag_list;
   1137                 }
   1138                 FREE(feature_tag_list_buffer);
   1139             }
   1140         }
   1141     }
   1142 
   1143     return true;
   1144 }
   1145 
   1146 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
   1147 {
   1148     HB_GlyphAttributes *tmpAttributes;
   1149     unsigned int *tmpLogClusters;
   1150 
   1151     HB_Face face = item->face;
   1152 
   1153     face->length = item->num_glyphs;
   1154 
   1155     hb_buffer_clear(face->buffer);
   1156 
   1157     tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
   1158     if (!tmpAttributes)
   1159         return false;
   1160     face->tmpAttributes = tmpAttributes;
   1161 
   1162     tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
   1163     if (!tmpLogClusters)
   1164         return false;
   1165     face->tmpLogClusters = tmpLogClusters;
   1166 
   1167     for (int i = 0; i < face->length; ++i) {
   1168         hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
   1169         face->tmpAttributes[i] = item->attributes[i];
   1170         face->tmpLogClusters[i] = item->log_clusters[i];
   1171     }
   1172 
   1173 #ifdef OT_DEBUG
   1174     DEBUG("-----------------------------------------");
   1175 //     DEBUG("log clusters before shaping:");
   1176 //     for (int j = 0; j < length; j++)
   1177 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
   1178     DEBUG("original glyphs: %p", item->glyphs);
   1179     for (int i = 0; i < length; ++i)
   1180         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
   1181 //     dump_string(hb_buffer);
   1182 #endif
   1183 
   1184     face->glyphs_substituted = false;
   1185     if (face->gsub) {
   1186         unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
   1187         if (error && error != HB_Err_Not_Covered)
   1188             return false;
   1189         face->glyphs_substituted = (error != HB_Err_Not_Covered);
   1190     }
   1191 
   1192 #ifdef OT_DEBUG
   1193 //     DEBUG("log clusters before shaping:");
   1194 //     for (int j = 0; j < length; j++)
   1195 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
   1196     DEBUG("shaped glyphs:");
   1197     for (int i = 0; i < length; ++i)
   1198         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
   1199     DEBUG("-----------------------------------------");
   1200 //     dump_string(hb_buffer);
   1201 #endif
   1202 
   1203     return true;
   1204 }
   1205 
   1206 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
   1207 {
   1208     HB_Face face = item->face;
   1209 
   1210     bool glyphs_positioned = false;
   1211     if (face->gpos) {
   1212         if (face->buffer->positions)
   1213             memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
   1214         // #### check that passing "false,false" is correct
   1215         glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
   1216     }
   1217 
   1218     if (!face->glyphs_substituted && !glyphs_positioned) {
   1219         HB_GetGlyphAdvances(item);
   1220         return true; // nothing to do for us
   1221     }
   1222 
   1223     // make sure we have enough space to write everything back
   1224     if (availableGlyphs < (int)face->buffer->in_length) {
   1225         item->num_glyphs = face->buffer->in_length;
   1226         return false;
   1227     }
   1228 
   1229     HB_Glyph *glyphs = item->glyphs;
   1230     HB_GlyphAttributes *attributes = item->attributes;
   1231 
   1232     for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
   1233         glyphs[i] = face->buffer->in_string[i].gindex;
   1234         attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
   1235         if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
   1236             attributes[i].clusterStart = false;
   1237     }
   1238     item->num_glyphs = face->buffer->in_length;
   1239 
   1240     if (doLogClusters && face->glyphs_substituted) {
   1241         // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
   1242         unsigned short *logClusters = item->log_clusters;
   1243         int clusterStart = 0;
   1244         int oldCi = 0;
   1245         // #### the reconstruction of the logclusters currently does not work if the original string
   1246         // contains surrogate pairs
   1247         for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
   1248             int ci = face->buffer->in_string[i].cluster;
   1249             //         DEBUG("   ci[%d] = %d mark=%d, cmb=%d, cs=%d",
   1250             //                i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
   1251             if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
   1252                 for (int j = oldCi; j < ci; j++)
   1253                     logClusters[j] = clusterStart;
   1254                 clusterStart = i;
   1255                 oldCi = ci;
   1256             }
   1257         }
   1258         for (int j = oldCi; j < face->length; j++)
   1259             logClusters[j] = clusterStart;
   1260     }
   1261 
   1262     // calulate the advances for the shaped glyphs
   1263 //     DEBUG("unpositioned: ");
   1264 
   1265     // positioning code:
   1266     if (glyphs_positioned) {
   1267         HB_GetGlyphAdvances(item);
   1268         HB_Position positions = face->buffer->positions;
   1269         HB_Fixed *advances = item->advances;
   1270 
   1271 //         DEBUG("positioned glyphs:");
   1272         for (unsigned int i = 0; i < face->buffer->in_length; i++) {
   1273 //             DEBUG("    %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
   1274 //                    glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
   1275 //                    (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
   1276 //                    (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
   1277 //                    positions[i].back, positions[i].new_advance);
   1278 
   1279             HB_Fixed adjustment = positions[i].x_advance;
   1280 
   1281             if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
   1282                 adjustment = HB_FIXED_ROUND(adjustment);
   1283 
   1284             if (positions[i].new_advance) {
   1285                 advances[i] = adjustment;
   1286             } else {
   1287                 advances[i] += adjustment;
   1288             }
   1289 
   1290             int back = 0;
   1291             HB_FixedPoint *offsets = item->offsets;
   1292             offsets[i].x = positions[i].x_pos;
   1293             offsets[i].y = positions[i].y_pos;
   1294             while (positions[i - back].back) {
   1295                 back += positions[i - back].back;
   1296                 offsets[i].x += positions[i - back].x_pos;
   1297                 offsets[i].y += positions[i - back].y_pos;
   1298             }
   1299             offsets[i].y = -offsets[i].y;
   1300 
   1301             if (item->item.bidiLevel % 2) {
   1302                 // ### may need to go back multiple glyphs like in ltr
   1303                 back = positions[i].back;
   1304                 while (back--)
   1305                     offsets[i].x -= advances[i-back];
   1306             } else {
   1307                 back = 0;
   1308                 while (positions[i - back].back) {
   1309                     back += positions[i - back].back;
   1310                     offsets[i].x -= advances[i-back];
   1311                 }
   1312             }
   1313 //             DEBUG("   ->\tadv=%d\tpos=(%d/%d)",
   1314 //                    glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
   1315              DEBUG("Glyph offset[%d] x:%d, y: %d)", i, offsets[i].x, offsets[i].y);
   1316         }
   1317         item->kerning_applied = face->has_opentype_kerning;
   1318     } else {
   1319         HB_HeuristicPosition(item);
   1320     }
   1321 
   1322 #ifdef OT_DEBUG
   1323     if (doLogClusters) {
   1324         DEBUG("log clusters after shaping:");
   1325         for (int j = 0; j < length; j++)
   1326             DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
   1327     }
   1328     DEBUG("final glyphs:");
   1329     for (int i = 0; i < (int)hb_buffer->in_length; ++i)
   1330         DEBUG("   glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
   1331                glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
   1332                glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
   1333                glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
   1334                glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
   1335     DEBUG("-----------------------------------------");
   1336 #endif
   1337     return true;
   1338 }
   1339 
   1340 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
   1341 {
   1342     HB_Bool result = false;
   1343     if (shaper_item->num_glyphs < shaper_item->item.length) {
   1344         shaper_item->num_glyphs = shaper_item->item.length;
   1345         return false;
   1346     }
   1347     assert(shaper_item->item.script < HB_ScriptCount);
   1348     result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
   1349     shaper_item->glyphIndicesPresent = false;
   1350     return result;
   1351 }
   1352 
   1353