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