Home | History | Annotate | Download | only in hb-old
      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 // Basic processing
     38 //
     39 // --------------------------------------------------------------------------------------------------------------------------------------------
     40 
     41 static inline void positionCluster(HB_ShaperItem *item, int gfrom,  int glast)
     42 {
     43     int nmarks = glast - gfrom;
     44     assert(nmarks > 0);
     45 
     46     HB_Glyph *glyphs = item->glyphs;
     47     HB_GlyphAttributes *attributes = item->attributes;
     48 
     49     HB_GlyphMetrics baseMetrics;
     50     item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
     51 
     52     if (item->item.script == HB_Script_Hebrew
     53         && (-baseMetrics.y) > baseMetrics.height)
     54         // we need to attach below the baseline, because of the hebrew iud.
     55         baseMetrics.height = -baseMetrics.y;
     56 
     57 //     qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
     58 //     qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
     59 
     60     HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
     61     HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
     62     if (size > HB_FIXED_CONSTANT(4))
     63         offsetBase += HB_FIXED_CONSTANT(4);
     64     else
     65         offsetBase += size;
     66     offsetBase = -offsetBase;
     67     //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
     68 //     qDebug("offset = %f", offsetBase);
     69 
     70     bool rightToLeft = item->item.bidiLevel % 2;
     71 
     72     int i;
     73     unsigned char lastCmb = 0;
     74     HB_GlyphMetrics attachmentRect;
     75     memset(&attachmentRect, 0, sizeof(attachmentRect));
     76 
     77     for(i = 1; i <= nmarks; i++) {
     78         HB_Glyph mark = glyphs[gfrom+i];
     79         HB_GlyphMetrics markMetrics;
     80         item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
     81         HB_FixedPoint p;
     82         p.x = p.y = 0;
     83 //          qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
     84 
     85         HB_Fixed offset = offsetBase;
     86         unsigned char cmb = attributes[gfrom+i].combiningClass;
     87 
     88         // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
     89         // bits  in the glyphAttributes structure.
     90         if (cmb < 200) {
     91             // fixed position classes. We approximate by mapping to one of the others.
     92             // currently I added only the ones for arabic, hebrew, lao and thai.
     93 
     94             // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
     95 
     96             // add a bit more offset to arabic, a bit hacky
     97             if (cmb >= 27 && cmb <= 36 && offset < 3)
     98                 offset +=1;
     99             // below
    100             if ((cmb >= 10 && cmb <= 18) ||
    101                  cmb == 20 || cmb == 22 ||
    102                  cmb == 29 || cmb == 32)
    103                 cmb = HB_Combining_Below;
    104             // above
    105             else if (cmb == 23 || cmb == 27 || cmb == 28 ||
    106                       cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
    107                 cmb = HB_Combining_Above;
    108             //below-right
    109             else if (cmb == 9 || cmb == 103 || cmb == 118)
    110                 cmb = HB_Combining_BelowRight;
    111             // above-right
    112             else if (cmb == 24 || cmb == 107 || cmb == 122)
    113                 cmb = HB_Combining_AboveRight;
    114             else if (cmb == 25)
    115                 cmb = HB_Combining_AboveLeft;
    116             // fixed:
    117             //  19 21
    118 
    119         }
    120 
    121         // combining marks of different class don't interact. Reset the rectangle.
    122         if (cmb != lastCmb) {
    123             //qDebug("resetting rect");
    124             attachmentRect = baseMetrics;
    125         }
    126 
    127         switch(cmb) {
    128         case HB_Combining_DoubleBelow:
    129                 // ### wrong in rtl context!
    130         case HB_Combining_BelowLeft:
    131             p.y += offset;
    132         case HB_Combining_BelowLeftAttached:
    133             p.x += attachmentRect.x - markMetrics.x;
    134             p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
    135             break;
    136         case HB_Combining_Below:
    137             p.y += offset;
    138         case HB_Combining_BelowAttached:
    139             p.x += attachmentRect.x - markMetrics.x;
    140             p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
    141 
    142             p.x += (attachmentRect.width - markMetrics.width) / 2;
    143             break;
    144         case HB_Combining_BelowRight:
    145             p.y += offset;
    146         case HB_Combining_BelowRightAttached:
    147             p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
    148             p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
    149             break;
    150         case HB_Combining_Left:
    151             p.x -= offset;
    152         case HB_Combining_LeftAttached:
    153             break;
    154         case HB_Combining_Right:
    155             p.x += offset;
    156         case HB_Combining_RightAttached:
    157             break;
    158         case HB_Combining_DoubleAbove:
    159             // ### wrong in RTL context!
    160         case HB_Combining_AboveLeft:
    161             p.y -= offset;
    162         case HB_Combining_AboveLeftAttached:
    163             p.x += attachmentRect.x - markMetrics.x;
    164             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
    165             break;
    166         case HB_Combining_Above:
    167             p.y -= offset;
    168         case HB_Combining_AboveAttached:
    169             p.x += attachmentRect.x - markMetrics.x;
    170             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
    171 
    172             p.x += (attachmentRect.width - markMetrics.width) / 2;
    173             break;
    174         case HB_Combining_AboveRight:
    175             p.y -= offset;
    176         case HB_Combining_AboveRightAttached:
    177             p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
    178             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
    179             break;
    180 
    181         case HB_Combining_IotaSubscript:
    182             default:
    183                 break;
    184         }
    185 //          qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
    186         markMetrics.x += p.x;
    187         markMetrics.y += p.y;
    188 
    189         HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
    190         unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
    191         unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
    192         unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
    193         unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
    194         attachmentRect = unitedAttachmentRect;
    195 
    196         lastCmb = cmb;
    197         if (rightToLeft) {
    198             item->offsets[gfrom+i].x = p.x;
    199             item->offsets[gfrom+i].y = p.y;
    200         } else {
    201             item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
    202             item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
    203         }
    204         item->advances[gfrom+i] = 0;
    205     }
    206 }
    207 
    208 void HB_HeuristicPosition(HB_ShaperItem *item)
    209 {
    210     HB_GetGlyphAdvances(item);
    211     HB_GlyphAttributes *attributes = item->attributes;
    212 
    213     int cEnd = -1;
    214     int i = item->num_glyphs;
    215     while (i--) {
    216         if (cEnd == -1 && attributes[i].mark) {
    217             cEnd = i;
    218         } else if (cEnd != -1 && !attributes[i].mark) {
    219             positionCluster(item, i, cEnd);
    220             cEnd = -1;
    221         }
    222     }
    223 }
    224 
    225 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
    226 // and no reordering.
    227 // also computes logClusters heuristically
    228 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
    229 {
    230     const HB_UChar16 *uc = item->string + item->item.pos;
    231     hb_uint32 length = item->item.length;
    232 
    233     // ### zeroWidth and justification are missing here!!!!!
    234 
    235     assert(item->num_glyphs <= length);
    236 
    237 //     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
    238     HB_GlyphAttributes *attributes = item->attributes;
    239     unsigned short *logClusters = item->log_clusters;
    240 
    241     hb_uint32 glyph_pos = 0;
    242     hb_uint32 i;
    243     for (i = 0; i < length; i++) {
    244         if (HB_IsHighSurrogate(uc[i]) && i < length - 1
    245             && HB_IsLowSurrogate(uc[i + 1])) {
    246             logClusters[i] = glyph_pos;
    247             logClusters[++i] = glyph_pos;
    248         } else {
    249             logClusters[i] = glyph_pos;
    250         }
    251         ++glyph_pos;
    252     }
    253     assert(glyph_pos == item->num_glyphs);
    254 
    255     // first char in a run is never (treated as) a mark
    256     int cStart = 0;
    257     const bool symbolFont = item->face->isSymbolFont;
    258     attributes[0].mark = false;
    259     attributes[0].clusterStart = true;
    260     attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
    261 
    262     int pos = 0;
    263     HB_CharCategory lastCat;
    264     int dummy;
    265     HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
    266     for (i = 1; i < length; ++i) {
    267         if (logClusters[i] == pos)
    268             // same glyph
    269             continue;
    270         ++pos;
    271         while (pos < logClusters[i]) {
    272             attributes[pos] = attributes[pos-1];
    273             ++pos;
    274         }
    275         // hide soft-hyphens by default
    276         if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
    277             attributes[pos].dontPrint = true;
    278         HB_CharCategory cat;
    279         int cmb;
    280         HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
    281         if (cat != HB_Mark_NonSpacing) {
    282             attributes[pos].mark = false;
    283             attributes[pos].clusterStart = true;
    284             attributes[pos].combiningClass = 0;
    285             cStart = logClusters[i];
    286         } else {
    287             if (cmb == 0) {
    288                 // Fix 0 combining classes
    289                 if ((uc[pos] & 0xff00) == 0x0e00) {
    290                     // thai or lao
    291                     if (uc[pos] == 0xe31 ||
    292                          uc[pos] == 0xe34 ||
    293                          uc[pos] == 0xe35 ||
    294                          uc[pos] == 0xe36 ||
    295                          uc[pos] == 0xe37 ||
    296                          uc[pos] == 0xe47 ||
    297                          uc[pos] == 0xe4c ||
    298                          uc[pos] == 0xe4d ||
    299                          uc[pos] == 0xe4e) {
    300                         cmb = HB_Combining_AboveRight;
    301                     } else if (uc[pos] == 0xeb1 ||
    302                                 uc[pos] == 0xeb4 ||
    303                                 uc[pos] == 0xeb5 ||
    304                                 uc[pos] == 0xeb6 ||
    305                                 uc[pos] == 0xeb7 ||
    306                                 uc[pos] == 0xebb ||
    307                                 uc[pos] == 0xecc ||
    308                                 uc[pos] == 0xecd) {
    309                         cmb = HB_Combining_Above;
    310                     } else if (uc[pos] == 0xebc) {
    311                         cmb = HB_Combining_Below;
    312                     }
    313                 }
    314             }
    315 
    316             attributes[pos].mark = true;
    317             attributes[pos].clusterStart = false;
    318             attributes[pos].combiningClass = cmb;
    319             logClusters[i] = cStart;
    320         }
    321         // one gets an inter character justification point if the current char is not a non spacing mark.
    322         // as then the current char belongs to the last one and one gets a space justification point
    323         // after the space char.
    324         if (lastCat == HB_Separator_Space)
    325             attributes[pos-1].justification = HB_Space;
    326         else if (cat != HB_Mark_NonSpacing)
    327             attributes[pos-1].justification = HB_Character;
    328         else
    329             attributes[pos-1].justification = HB_NoJustification;
    330 
    331         lastCat = cat;
    332     }
    333     pos = logClusters[length-1];
    334     if (lastCat == HB_Separator_Space)
    335         attributes[pos].justification = HB_Space;
    336     else
    337         attributes[pos].justification = HB_Character;
    338 }
    339 
    340 #ifndef NO_OPENTYPE
    341 static const HB_OpenTypeFeature basic_features[] = {
    342     { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
    343     { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
    344     { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
    345     {0, 0}
    346 };
    347 #endif
    348 
    349 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
    350 {
    351     if (shaper_item->glyphIndicesPresent) {
    352         shaper_item->num_glyphs = shaper_item->initialGlyphCount;
    353         shaper_item->glyphIndicesPresent = false;
    354         return true;
    355     }
    356     return shaper_item->font->klass
    357            ->convertStringToGlyphIndices(shaper_item->font,
    358                                          shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
    359                                          shaper_item->glyphs, &shaper_item->num_glyphs,
    360                                          shaper_item->item.bidiLevel % 2);
    361 }
    362 
    363 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
    364 {
    365 #ifndef NO_OPENTYPE
    366     const int availableGlyphs = shaper_item->num_glyphs;
    367 #endif
    368 
    369     if (!HB_ConvertStringToGlyphIndices(shaper_item))
    370         return false;
    371 
    372     HB_HeuristicSetGlyphAttributes(shaper_item);
    373 
    374 #ifndef NO_OPENTYPE
    375     if (HB_SelectScript(shaper_item, basic_features)) {
    376         HB_OpenTypeShape(shaper_item, /*properties*/0);
    377         return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
    378     }
    379 #endif
    380 
    381     HB_HeuristicPosition(shaper_item);
    382     return true;
    383 }
    384 
    385 const HB_ScriptEngine HB_ScriptEngines[] = {
    386     // Common
    387     { HB_BasicShape},
    388     // Greek
    389     { HB_GreekShape},
    390     // Cyrillic
    391     { HB_BasicShape},
    392     // Armenian
    393     { HB_BasicShape},
    394     // Hebrew
    395     { HB_HebrewShape},
    396     // Arabic
    397     { HB_ArabicShape},
    398     // Syriac
    399     { HB_ArabicShape},
    400     // Thaana
    401     { HB_BasicShape},
    402     // Devanagari
    403     { HB_IndicShape},
    404     // Bengali
    405     { HB_IndicShape},
    406     // Gurmukhi
    407     { HB_IndicShape},
    408     // Gujarati
    409     { HB_IndicShape},
    410     // Oriya
    411     { HB_IndicShape},
    412     // Tamil
    413     { HB_IndicShape},
    414     // Telugu
    415     { HB_IndicShape},
    416     // Kannada
    417     { HB_IndicShape},
    418     // Malayalam
    419     { HB_IndicShape},
    420     // Sinhala
    421     { HB_IndicShape},
    422     // Thai
    423     { HB_BasicShape},
    424     // Lao
    425     { HB_BasicShape},
    426     // Tibetan
    427     { HB_TibetanShape},
    428     // Myanmar
    429     { HB_MyanmarShape},
    430     // Georgian
    431     { HB_BasicShape},
    432     // Hangul
    433     { HB_HangulShape},
    434     // Ogham
    435     { HB_BasicShape},
    436     // Runic
    437     { HB_BasicShape},
    438     // Khmer
    439     { HB_KhmerShape},
    440     // N'Ko
    441     { HB_ArabicShape}
    442 };
    443 
    444 
    445 static inline char *tag_to_string(HB_UInt tag)
    446 {
    447     static char string[5];
    448     string[0] = (tag >> 24)&0xff;
    449     string[1] = (tag >> 16)&0xff;
    450     string[2] = (tag >> 8)&0xff;
    451     string[3] = tag&0xff;
    452     string[4] = 0;
    453     return string;
    454 }
    455 
    456 #ifdef OT_DEBUG
    457 static void dump_string(HB_Buffer buffer)
    458 {
    459     for (uint i = 0; i < buffer->in_length; ++i) {
    460         qDebug("    %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
    461     }
    462 }
    463 #define DEBUG printf
    464 #else
    465 #define DEBUG if (1) ; else printf
    466 #endif
    467 
    468 #if 0
    469 #define DefaultLangSys 0xffff
    470 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
    471 #endif
    472 
    473 enum {
    474     RequiresGsub = 1,
    475     RequiresGpos = 2
    476 };
    477 
    478 struct OTScripts {
    479     unsigned int tag;
    480     int flags;
    481 };
    482 static const OTScripts ot_scripts [] = {
    483     // Common
    484     { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
    485     // Greek
    486     { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
    487     // Cyrillic
    488     { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
    489     // Armenian
    490     { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
    491     // Hebrew
    492     { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
    493     // Arabic
    494     { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
    495     // Syriac
    496     { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
    497     // Thaana
    498     { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
    499     // Devanagari
    500     { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
    501     // Bengali
    502     { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
    503     // Gurmukhi
    504     { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
    505     // Gujarati
    506     { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
    507     // Oriya
    508     { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
    509     // Tamil
    510     { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
    511     // Telugu
    512     { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
    513     // Kannada
    514     { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
    515     // Malayalam
    516     { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
    517     // Sinhala
    518     { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
    519     // Thai
    520     { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
    521     // Lao
    522     { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
    523     // Tibetan
    524     { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
    525     // Myanmar
    526     { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
    527     // Georgian
    528     { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
    529     // Hangul
    530     { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
    531     // Ogham
    532     { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
    533     // Runic
    534     { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
    535     // Khmer
    536     { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
    537     // N'Ko
    538     { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
    539 };
    540 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
    541 
    542 static HB_Bool checkScript(HB_Face face, int script)
    543 {
    544     assert(script < HB_ScriptCount);
    545 
    546     if (!face->gsub && !face->gpos)
    547         return false;
    548 
    549     unsigned int tag = ot_scripts[script].tag;
    550     int requirements = ot_scripts[script].flags;
    551 
    552     if (requirements & RequiresGsub) {
    553         if (!face->gsub)
    554             return false;
    555 
    556         HB_UShort script_index;
    557         HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
    558         if (error) {
    559             DEBUG("could not select script %d in GSub table: %d", (int)script, error);
    560             error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
    561             if (error)
    562                 return false;
    563         }
    564     }
    565 
    566     if (requirements & RequiresGpos) {
    567         if (!face->gpos)
    568             return false;
    569 
    570         HB_UShort script_index;
    571         HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
    572         if (error) {
    573             DEBUG("could not select script in gpos table: %d", error);
    574             error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
    575             if (error)
    576                 return false;
    577         }
    578 
    579     }
    580     return true;
    581 }
    582 
    583 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
    584 {
    585     HB_Error error;
    586     HB_UInt length = 0;
    587     HB_Stream stream = 0;
    588 
    589     if (!font)
    590         return 0;
    591 
    592     error = tableFunc(font, tag, 0, &length);
    593     if (error)
    594         return 0;
    595     stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
    596     if (!stream)
    597         return 0;
    598     stream->base = (HB_Byte*)malloc(length);
    599     if (!stream->base) {
    600         free(stream);
    601         return 0;
    602     }
    603     error = tableFunc(font, tag, stream->base, &length);
    604     if (error) {
    605         _hb_close_stream(stream);
    606         return 0;
    607     }
    608     stream->size = length;
    609     stream->pos = 0;
    610     stream->cursor = NULL;
    611     return stream;
    612 }
    613 
    614 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
    615 {
    616     HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
    617     if (!face)
    618         return 0;
    619 
    620     face->isSymbolFont = false;
    621     face->gdef = 0;
    622     face->gpos = 0;
    623     face->gsub = 0;
    624     face->current_script = HB_ScriptCount;
    625     face->current_flags = HB_ShaperFlag_Default;
    626     face->has_opentype_kerning = false;
    627     face->tmpAttributes = 0;
    628     face->tmpLogClusters = 0;
    629     face->glyphs_substituted = false;
    630     face->buffer = 0;
    631 
    632     HB_Error error = HB_Err_Ok;
    633     HB_Stream stream;
    634     HB_Stream gdefStream;
    635 
    636     gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
    637     error = HB_Err_Not_Covered;
    638     if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
    639         //DEBUG("error loading gdef table: %d", error);
    640         face->gdef = 0;
    641     }
    642 
    643     //DEBUG() << "trying to load gsub table";
    644     stream = getTableStream(font, tableFunc, TTAG_GSUB);
    645     error = HB_Err_Not_Covered;
    646     if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
    647         face->gsub = 0;
    648         if (error != HB_Err_Not_Covered) {
    649             //DEBUG("error loading gsub table: %d", error);
    650         } else {
    651             //DEBUG("face doesn't have a gsub table");
    652         }
    653     }
    654     _hb_close_stream(stream);
    655 
    656     stream = getTableStream(font, tableFunc, TTAG_GPOS);
    657     error = HB_Err_Not_Covered;
    658     if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
    659         face->gpos = 0;
    660         DEBUG("error loading gpos table: %d", error);
    661     }
    662     _hb_close_stream(stream);
    663 
    664     _hb_close_stream(gdefStream);
    665 
    666     for (unsigned int i = 0; i < HB_ScriptCount; ++i)
    667         face->supported_scripts[i] = checkScript(face, i);
    668 
    669     if (HB_Buffer_new(&face->buffer) != HB_Err_Ok) {
    670         HB_FreeFace(face);
    671         return 0;
    672     }
    673 
    674     return face;
    675 }
    676 
    677 void HB_FreeFace(HB_Face face)
    678 {
    679     if (!face)
    680         return;
    681     if (face->gpos)
    682         HB_Done_GPOS_Table(face->gpos);
    683     if (face->gsub)
    684         HB_Done_GSUB_Table(face->gsub);
    685     if (face->gdef)
    686         HB_Done_GDEF_Table(face->gdef);
    687     if (face->buffer)
    688         HB_Buffer_free(face->buffer);
    689     if (face->tmpAttributes)
    690         free(face->tmpAttributes);
    691     if (face->tmpLogClusters)
    692         free(face->tmpLogClusters);
    693     free(face);
    694 }
    695 
    696 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
    697 {
    698     HB_Script script = shaper_item->item.script;
    699 
    700     if (!shaper_item->face->supported_scripts[script])
    701         return false;
    702 
    703     HB_Face face = shaper_item->face;
    704     if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
    705         return true;
    706 
    707     face->current_script = script;
    708     face->current_flags = shaper_item->shaperFlags;
    709 
    710     assert(script < HB_ScriptCount);
    711     // find script in our list of supported scripts.
    712     unsigned int tag = ot_scripts[script].tag;
    713 
    714     if (face->gsub && features) {
    715 #ifdef OT_DEBUG
    716         {
    717             HB_FeatureList featurelist = face->gsub->FeatureList;
    718             int numfeatures = featurelist.FeatureCount;
    719             DEBUG("gsub table has %d features", numfeatures);
    720             for (int i = 0; i < numfeatures; i++) {
    721                 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
    722                 DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
    723             }
    724         }
    725 #endif
    726         HB_GSUB_Clear_Features(face->gsub);
    727         HB_UShort script_index;
    728         HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
    729         if (!error) {
    730             DEBUG("script %s has script index %d", tag_to_string(script), script_index);
    731             while (features->tag) {
    732                 HB_UShort feature_index;
    733                 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
    734                 if (!error) {
    735                     DEBUG("  adding feature %s", tag_to_string(features->tag));
    736                     HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
    737                 }
    738                 ++features;
    739             }
    740         }
    741     }
    742 
    743     // reset
    744     face->has_opentype_kerning = false;
    745 
    746     if (face->gpos) {
    747         HB_GPOS_Clear_Features(face->gpos);
    748         HB_UShort script_index;
    749         HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
    750         if (!error) {
    751 #ifdef OT_DEBUG
    752             {
    753                 HB_FeatureList featurelist = face->gpos->FeatureList;
    754                 int numfeatures = featurelist.FeatureCount;
    755                 DEBUG("gpos table has %d features", numfeatures);
    756                 for(int i = 0; i < numfeatures; i++) {
    757                     HB_FeatureRecord *r = featurelist.FeatureRecord + i;
    758                     HB_UShort feature_index;
    759                     HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
    760                     DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
    761                 }
    762             }
    763 #endif
    764             HB_UInt *feature_tag_list_buffer;
    765             error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
    766             if (!error) {
    767                 HB_UInt *feature_tag_list = feature_tag_list_buffer;
    768                 while (*feature_tag_list) {
    769                     HB_UShort feature_index;
    770                     if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
    771                         if (face->current_flags & HB_ShaperFlag_NoKerning) {
    772                             ++feature_tag_list;
    773                             continue;
    774                         }
    775                         face->has_opentype_kerning = true;
    776                     }
    777                     error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
    778                     if (!error)
    779                         HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
    780                     ++feature_tag_list;
    781                 }
    782                 FREE(feature_tag_list_buffer);
    783             }
    784         }
    785     }
    786 
    787     return true;
    788 }
    789 
    790 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
    791 {
    792     HB_GlyphAttributes *tmpAttributes;
    793     unsigned int *tmpLogClusters;
    794 
    795     HB_Face face = item->face;
    796 
    797     face->length = item->num_glyphs;
    798 
    799     HB_Buffer_clear(face->buffer);
    800 
    801     tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
    802     if (!tmpAttributes)
    803         return false;
    804     face->tmpAttributes = tmpAttributes;
    805 
    806     tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
    807     if (!tmpLogClusters)
    808         return false;
    809     face->tmpLogClusters = tmpLogClusters;
    810 
    811     for (int i = 0; i < face->length; ++i) {
    812         HB_Buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
    813         face->tmpAttributes[i] = item->attributes[i];
    814         face->tmpLogClusters[i] = item->log_clusters[i];
    815     }
    816 
    817 #ifdef OT_DEBUG
    818     DEBUG("-----------------------------------------");
    819 //     DEBUG("log clusters before shaping:");
    820 //     for (int j = 0; j < length; j++)
    821 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
    822     DEBUG("original glyphs: %p", item->glyphs);
    823     for (int i = 0; i < length; ++i)
    824         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
    825 //     dump_string(hb_buffer);
    826 #endif
    827 
    828     face->glyphs_substituted = false;
    829     if (face->gsub) {
    830         unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
    831         if (error && error != HB_Err_Not_Covered)
    832             return false;
    833         face->glyphs_substituted = (error != HB_Err_Not_Covered);
    834     }
    835 
    836 #ifdef OT_DEBUG
    837 //     DEBUG("log clusters before shaping:");
    838 //     for (int j = 0; j < length; j++)
    839 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
    840     DEBUG("shaped glyphs:");
    841     for (int i = 0; i < length; ++i)
    842         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
    843     DEBUG("-----------------------------------------");
    844 //     dump_string(hb_buffer);
    845 #endif
    846 
    847     return true;
    848 }
    849 
    850 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
    851 {
    852     HB_Face face = item->face;
    853 
    854     bool glyphs_positioned = false;
    855     if (face->gpos) {
    856         if (face->buffer->positions)
    857             memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
    858         // #### check that passing "false,false" is correct
    859         glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
    860     }
    861 
    862     if (!face->glyphs_substituted && !glyphs_positioned) {
    863         HB_GetGlyphAdvances(item);
    864         return true; // nothing to do for us
    865     }
    866 
    867     // make sure we have enough space to write everything back
    868     if (availableGlyphs < (int)face->buffer->in_length) {
    869         item->num_glyphs = face->buffer->in_length;
    870         return false;
    871     }
    872 
    873     HB_Glyph *glyphs = item->glyphs;
    874     HB_GlyphAttributes *attributes = item->attributes;
    875 
    876     for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
    877         glyphs[i] = face->buffer->in_string[i].gindex;
    878         attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
    879         if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
    880             attributes[i].clusterStart = false;
    881     }
    882     item->num_glyphs = face->buffer->in_length;
    883 
    884     if (doLogClusters && face->glyphs_substituted) {
    885         // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
    886         unsigned short *logClusters = item->log_clusters;
    887         int clusterStart = 0;
    888         int oldCi = 0;
    889         // #### the reconstruction of the logclusters currently does not work if the original string
    890         // contains surrogate pairs
    891         for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
    892             int ci = face->buffer->in_string[i].cluster;
    893             //         DEBUG("   ci[%d] = %d mark=%d, cmb=%d, cs=%d",
    894             //                i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
    895             if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
    896                 for (int j = oldCi; j < ci; j++)
    897                     logClusters[j] = clusterStart;
    898                 clusterStart = i;
    899                 oldCi = ci;
    900             }
    901         }
    902         for (int j = oldCi; j < face->length; j++)
    903             logClusters[j] = clusterStart;
    904     }
    905 
    906     // calulate the advances for the shaped glyphs
    907 //     DEBUG("unpositioned: ");
    908 
    909     // positioning code:
    910     if (glyphs_positioned) {
    911         HB_GetGlyphAdvances(item);
    912         HB_Position positions = face->buffer->positions;
    913         HB_Fixed *advances = item->advances;
    914 
    915 //         DEBUG("positioned glyphs:");
    916         for (unsigned int i = 0; i < face->buffer->in_length; i++) {
    917 //             DEBUG("    %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
    918 //                    glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
    919 //                    (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
    920 //                    (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
    921 //                    positions[i].back, positions[i].new_advance);
    922 
    923             HB_Fixed adjustment = positions[i].x_advance;
    924 
    925             if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
    926                 adjustment = HB_FIXED_ROUND(adjustment);
    927 
    928             if (positions[i].new_advance) {
    929                 ; //advances[i] = adjustment;
    930             } else {
    931                 advances[i] += adjustment;
    932             }
    933 
    934             int back = 0;
    935             HB_FixedPoint *offsets = item->offsets;
    936             offsets[i].x = positions[i].x_pos;
    937             offsets[i].y = positions[i].y_pos;
    938             while (positions[i - back].back) {
    939                 back += positions[i - back].back;
    940                 offsets[i].x += positions[i - back].x_pos;
    941                 offsets[i].y += positions[i - back].y_pos;
    942             }
    943             offsets[i].y = -offsets[i].y;
    944 
    945             if (item->item.bidiLevel % 2) {
    946                 // ### may need to go back multiple glyphs like in ltr
    947                 back = positions[i].back;
    948                 while (back--)
    949                     offsets[i].x -= advances[i-back];
    950             } else {
    951                 back = 0;
    952                 while (positions[i - back].back) {
    953                     back += positions[i - back].back;
    954                     offsets[i].x -= advances[i-back];
    955                 }
    956             }
    957 //             DEBUG("   ->\tadv=%d\tpos=(%d/%d)",
    958 //                    glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
    959         }
    960         item->kerning_applied = face->has_opentype_kerning;
    961     } else {
    962         HB_HeuristicPosition(item);
    963     }
    964 
    965 #ifdef OT_DEBUG
    966     if (doLogClusters) {
    967         DEBUG("log clusters after shaping:");
    968         for (int j = 0; j < length; j++)
    969             DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
    970     }
    971     DEBUG("final glyphs:");
    972     for (int i = 0; i < (int)hb_buffer->in_length; ++i)
    973         DEBUG("   glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
    974                glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
    975                glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
    976                glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
    977                glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
    978     DEBUG("-----------------------------------------");
    979 #endif
    980     return true;
    981 }
    982 
    983 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
    984 {
    985     HB_Bool result = false;
    986     if (shaper_item->num_glyphs < shaper_item->item.length) {
    987         shaper_item->num_glyphs = shaper_item->item.length;
    988         return false;
    989     }
    990     assert(shaper_item->item.script < HB_ScriptCount);
    991     result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
    992     shaper_item->glyphIndicesPresent = false;
    993     return result;
    994 }
    995