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