Home | History | Annotate | Download | only in layout
      1 
      2 /*
      3  *
      4  * (C) Copyright IBM Corp. and others 1998-2013 - All Rights Reserved
      5  *
      6  */
      7 
      8 #include "LETypes.h"
      9 #include "LEScripts.h"
     10 #include "LELanguages.h"
     11 
     12 #include "LayoutEngine.h"
     13 #include "CanonShaping.h"
     14 #include "OpenTypeLayoutEngine.h"
     15 #include "ScriptAndLanguageTags.h"
     16 #include "CharSubstitutionFilter.h"
     17 
     18 #include "GlyphSubstitutionTables.h"
     19 #include "GlyphDefinitionTables.h"
     20 #include "GlyphPositioningTables.h"
     21 
     22 #include "LEGlyphStorage.h"
     23 #include "GlyphPositionAdjustments.h"
     24 
     25 #include "GDEFMarkFilter.h"
     26 
     27 #include "KernTable.h"
     28 
     29 U_NAMESPACE_BEGIN
     30 
     31 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OpenTypeLayoutEngine)
     32 
     33 #define ccmpFeatureTag LE_CCMP_FEATURE_TAG
     34 #define ligaFeatureTag LE_LIGA_FEATURE_TAG
     35 #define cligFeatureTag LE_CLIG_FEATURE_TAG
     36 #define kernFeatureTag LE_KERN_FEATURE_TAG
     37 #define markFeatureTag LE_MARK_FEATURE_TAG
     38 #define mkmkFeatureTag LE_MKMK_FEATURE_TAG
     39 #define loclFeatureTag LE_LOCL_FEATURE_TAG
     40 #define caltFeatureTag LE_CALT_FEATURE_TAG
     41 
     42 #define dligFeatureTag LE_DLIG_FEATURE_TAG
     43 #define rligFeatureTag LE_RLIG_FEATURE_TAG
     44 #define paltFeatureTag LE_PALT_FEATURE_TAG
     45 
     46 #define hligFeatureTag LE_HLIG_FEATURE_TAG
     47 #define smcpFeatureTag LE_SMCP_FEATURE_TAG
     48 #define fracFeatureTag LE_FRAC_FEATURE_TAG
     49 #define afrcFeatureTag LE_AFRC_FEATURE_TAG
     50 #define zeroFeatureTag LE_ZERO_FEATURE_TAG
     51 #define swshFeatureTag LE_SWSH_FEATURE_TAG
     52 #define cswhFeatureTag LE_CSWH_FEATURE_TAG
     53 #define saltFeatureTag LE_SALT_FEATURE_TAG
     54 #define naltFeatureTag LE_NALT_FEATURE_TAG
     55 #define rubyFeatureTag LE_RUBY_FEATURE_TAG
     56 #define ss01FeatureTag LE_SS01_FEATURE_TAG
     57 #define ss02FeatureTag LE_SS02_FEATURE_TAG
     58 #define ss03FeatureTag LE_SS03_FEATURE_TAG
     59 #define ss04FeatureTag LE_SS04_FEATURE_TAG
     60 #define ss05FeatureTag LE_SS05_FEATURE_TAG
     61 #define ss06FeatureTag LE_SS06_FEATURE_TAG
     62 #define ss07FeatureTag LE_SS07_FEATURE_TAG
     63 
     64 #define ccmpFeatureMask 0x80000000UL
     65 #define ligaFeatureMask 0x40000000UL
     66 #define cligFeatureMask 0x20000000UL
     67 #define kernFeatureMask 0x10000000UL
     68 #define paltFeatureMask 0x08000000UL
     69 #define markFeatureMask 0x04000000UL
     70 #define mkmkFeatureMask 0x02000000UL
     71 #define loclFeatureMask 0x01000000UL
     72 #define caltFeatureMask 0x00800000UL
     73 
     74 #define dligFeatureMask 0x00400000UL
     75 #define rligFeatureMask 0x00200000UL
     76 #define hligFeatureMask 0x00100000UL
     77 #define smcpFeatureMask 0x00080000UL
     78 #define fracFeatureMask 0x00040000UL
     79 #define afrcFeatureMask 0x00020000UL
     80 #define zeroFeatureMask 0x00010000UL
     81 #define swshFeatureMask 0x00008000UL
     82 #define cswhFeatureMask 0x00004000UL
     83 #define saltFeatureMask 0x00002000UL
     84 #define naltFeatureMask 0x00001000UL
     85 #define rubyFeatureMask 0x00000800UL
     86 #define ss01FeatureMask 0x00000400UL
     87 #define ss02FeatureMask 0x00000200UL
     88 #define ss03FeatureMask 0x00000100UL
     89 #define ss04FeatureMask 0x00000080UL
     90 #define ss05FeatureMask 0x00000040UL
     91 #define ss06FeatureMask 0x00000020UL
     92 #define ss07FeatureMask 0x00000010UL
     93 
     94 #define minimalFeatures     (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask)
     95 
     96 static const FeatureMap featureMap[] =
     97 {
     98     {ccmpFeatureTag, ccmpFeatureMask},
     99     {ligaFeatureTag, ligaFeatureMask},
    100     {cligFeatureTag, cligFeatureMask},
    101     {kernFeatureTag, kernFeatureMask},
    102     {paltFeatureTag, paltFeatureMask},
    103     {markFeatureTag, markFeatureMask},
    104     {mkmkFeatureTag, mkmkFeatureMask},
    105     {loclFeatureTag, loclFeatureMask},
    106     {caltFeatureTag, caltFeatureMask},
    107     {hligFeatureTag, hligFeatureMask},
    108     {smcpFeatureTag, smcpFeatureMask},
    109     {fracFeatureTag, fracFeatureMask},
    110     {afrcFeatureTag, afrcFeatureMask},
    111     {zeroFeatureTag, zeroFeatureMask},
    112     {swshFeatureTag, swshFeatureMask},
    113     {cswhFeatureTag, cswhFeatureMask},
    114     {saltFeatureTag, saltFeatureMask},
    115     {naltFeatureTag, naltFeatureMask},
    116     {rubyFeatureTag, rubyFeatureMask},
    117     {ss01FeatureTag, ss01FeatureMask},
    118     {ss02FeatureTag, ss02FeatureMask},
    119     {ss03FeatureTag, ss03FeatureMask},
    120     {ss04FeatureTag, ss04FeatureMask},
    121     {ss05FeatureTag, ss05FeatureMask},
    122     {ss06FeatureTag, ss06FeatureMask},
    123     {ss07FeatureTag, ss07FeatureMask}
    124 };
    125 
    126 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
    127 
    128 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
    129                      le_int32 typoFlags, const LEReferenceTo<GlyphSubstitutionTableHeader> &gsubTable, LEErrorCode &success)
    130     : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures),
    131       fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE),
    132       fGSUBTable(gsubTable),
    133       fGDEFTable(fontInstance, LE_GDEF_TABLE_TAG, success),
    134       fGPOSTable(fontInstance, LE_GPOS_TABLE_TAG, success), fSubstitutionFilter(NULL)
    135 {
    136     applyTypoFlags();
    137 
    138     setScriptAndLanguageTags();
    139 
    140 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
    141 //    if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
    142     if (!fGPOSTable.isEmpty()&& !fGPOSTable->coversScript(fGPOSTable, fScriptTag, success)) {
    143       fGPOSTable.clear(); // already loaded
    144     }
    145 }
    146 
    147 void OpenTypeLayoutEngine::applyTypoFlags() {
    148     const le_int32& typoFlags = fTypoFlags;
    149     const LEFontInstance *fontInstance = fFontInstance;
    150 
    151     switch (typoFlags & (LE_SS01_FEATURE_FLAG
    152                          | LE_SS02_FEATURE_FLAG
    153                          | LE_SS03_FEATURE_FLAG
    154                          | LE_SS04_FEATURE_FLAG
    155                          | LE_SS05_FEATURE_FLAG
    156                          | LE_SS06_FEATURE_FLAG
    157                          | LE_SS07_FEATURE_FLAG)) {
    158         case LE_SS01_FEATURE_FLAG:
    159             fFeatureMask |= ss01FeatureMask;
    160             break;
    161         case LE_SS02_FEATURE_FLAG:
    162             fFeatureMask |= ss02FeatureMask;
    163             break;
    164         case LE_SS03_FEATURE_FLAG:
    165             fFeatureMask |= ss03FeatureMask;
    166             break;
    167         case LE_SS04_FEATURE_FLAG:
    168             fFeatureMask |= ss04FeatureMask;
    169             break;
    170         case LE_SS05_FEATURE_FLAG:
    171             fFeatureMask |= ss05FeatureMask;
    172             break;
    173         case LE_SS06_FEATURE_FLAG:
    174             fFeatureMask |= ss06FeatureMask;
    175             break;
    176         case LE_SS07_FEATURE_FLAG:
    177             fFeatureMask |= ss07FeatureMask;
    178             break;
    179     }
    180 
    181     if (typoFlags & LE_Kerning_FEATURE_FLAG) {
    182       fFeatureMask |= (kernFeatureMask | paltFeatureMask);
    183       // Convenience.
    184     }
    185     if (typoFlags & LE_Ligatures_FEATURE_FLAG) {
    186       fFeatureMask |= (ligaFeatureMask | cligFeatureMask);
    187       // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ?
    188     }
    189     if (typoFlags & LE_CLIG_FEATURE_FLAG) fFeatureMask |= cligFeatureMask;
    190     if (typoFlags & LE_DLIG_FEATURE_FLAG) fFeatureMask |= dligFeatureMask;
    191     if (typoFlags & LE_HLIG_FEATURE_FLAG) fFeatureMask |= hligFeatureMask;
    192     if (typoFlags & LE_LIGA_FEATURE_FLAG) fFeatureMask |= ligaFeatureMask;
    193     if (typoFlags & LE_RLIG_FEATURE_FLAG) fFeatureMask |= rligFeatureMask;
    194     if (typoFlags & LE_SMCP_FEATURE_FLAG) fFeatureMask |= smcpFeatureMask;
    195     if (typoFlags & LE_FRAC_FEATURE_FLAG) fFeatureMask |= fracFeatureMask;
    196     if (typoFlags & LE_AFRC_FEATURE_FLAG) fFeatureMask |= afrcFeatureMask;
    197     if (typoFlags & LE_ZERO_FEATURE_FLAG) fFeatureMask |= zeroFeatureMask;
    198     if (typoFlags & LE_SWSH_FEATURE_FLAG) fFeatureMask |= swshFeatureMask;
    199     if (typoFlags & LE_CSWH_FEATURE_FLAG) fFeatureMask |= cswhFeatureMask;
    200     if (typoFlags & LE_SALT_FEATURE_FLAG) fFeatureMask |= saltFeatureMask;
    201     if (typoFlags & LE_RUBY_FEATURE_FLAG) fFeatureMask |= rubyFeatureMask;
    202     if (typoFlags & LE_NALT_FEATURE_FLAG) {
    203       // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm
    204       fFeatureMask = naltFeatureMask;
    205     }
    206 
    207     if (typoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
    208       // This isn't a font feature, but requests a Char Substitution Filter
    209       fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
    210     }
    211 
    212 }
    213 
    214 void OpenTypeLayoutEngine::reset()
    215 {
    216     // NOTE: if we're called from
    217     // the destructor, LayoutEngine;:reset()
    218     // will have been called already by
    219     // LayoutEngine::~LayoutEngine()
    220     LayoutEngine::reset();
    221 }
    222 
    223 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
    224                        le_int32 typoFlags, LEErrorCode &success)
    225     : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE),
    226       fGSUBTable(), fGDEFTable(), fGPOSTable(), fSubstitutionFilter(NULL)
    227 {
    228   applyTypoFlags();
    229   setScriptAndLanguageTags();
    230 }
    231 
    232 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
    233 {
    234     if (fTypoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
    235         delete fSubstitutionFilter;
    236         fSubstitutionFilter = NULL;
    237     }
    238 
    239     reset();
    240 }
    241 
    242 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
    243 {
    244     if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
    245         return 0xFFFFFFFF;
    246     }
    247     return scriptTags[scriptCode];
    248 }
    249 
    250 LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode)
    251 {
    252 	switch (scriptCode) {
    253 		case bengScriptCode :    return bng2ScriptTag;
    254 		case devaScriptCode :    return dev2ScriptTag;
    255 		case gujrScriptCode :    return gjr2ScriptTag;
    256 		case guruScriptCode :    return gur2ScriptTag;
    257 		case kndaScriptCode :    return knd2ScriptTag;
    258 		case mlymScriptCode :    return mlm2ScriptTag;
    259 		case oryaScriptCode :    return ory2ScriptTag;
    260 		case tamlScriptCode :    return tml2ScriptTag;
    261 		case teluScriptCode :    return tel2ScriptTag;
    262 		default:                 return nullScriptTag;
    263 	}
    264 }
    265 
    266 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
    267 {
    268     if (languageCode < 0 || languageCode >= languageCodeCount) {
    269         return 0xFFFFFFFF;
    270     }
    271 
    272     return languageTags[languageCode];
    273 }
    274 
    275 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
    276 {
    277     fScriptTag  = getScriptTag(fScriptCode);
    278     fScriptTagV2 = getV2ScriptTag(fScriptCode);
    279     fLangSysTag = getLangSysTag(fLanguageCode);
    280 }
    281 
    282 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
    283                 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success)
    284 {
    285     if (LE_FAILURE(success)) {
    286         return 0;
    287     }
    288 
    289     if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
    290         success = LE_ILLEGAL_ARGUMENT_ERROR;
    291         return 0;
    292     }
    293 
    294     // This is the cheapest way to get mark reordering only for Hebrew.
    295     // We could just do the mark reordering for all scripts, but most
    296     // of them probably don't need it... Another option would be to
    297     // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
    298     // would need to do is mark reordering, so that seems like overkill.
    299     if (fScriptCode == hebrScriptCode) {
    300         outChars = LE_NEW_ARRAY(LEUnicode, count);
    301 
    302         if (outChars == NULL) {
    303             success = LE_MEMORY_ALLOCATION_ERROR;
    304             return 0;
    305         }
    306 
    307         if (LE_FAILURE(success)) {
    308             LE_DELETE_ARRAY(outChars);
    309             return 0;
    310         }
    311 
    312         CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage);
    313     }
    314 
    315     if (LE_FAILURE(success)) {
    316         return 0;
    317     }
    318 
    319     glyphStorage.allocateGlyphArray(count, rightToLeft, success);
    320     glyphStorage.allocateAuxData(success);
    321 
    322     for (le_int32 i = 0; i < count; i += 1) {
    323         glyphStorage.setAuxData(i, fFeatureMask, success);
    324     }
    325 
    326     return count;
    327 }
    328 
    329 // Input: characters, tags
    330 // Output: glyphs, char indices
    331 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
    332                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
    333 {
    334     if (LE_FAILURE(success)) {
    335         return 0;
    336     }
    337 
    338     if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
    339         success = LE_ILLEGAL_ARGUMENT_ERROR;
    340         return 0;
    341     }
    342 
    343     mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
    344 
    345     if (LE_FAILURE(success)) {
    346         return 0;
    347     }
    348 
    349     if (fGSUBTable.isValid()) {
    350       if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable, fScriptTagV2, fLangSysTag, success)) {
    351           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
    352                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
    353 
    354         } else {
    355           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
    356                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
    357         }
    358     }
    359 
    360     return count;
    361 }
    362 // Input: characters, tags
    363 // Output: glyphs, char indices
    364 le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft,
    365                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
    366 {
    367     if (LE_FAILURE(success)) {
    368         return 0;
    369     }
    370 
    371     if ( count < 0 || max < 0 ) {
    372         success = LE_ILLEGAL_ARGUMENT_ERROR;
    373         return 0;
    374     }
    375 
    376     if (fGSUBTable.isValid()) {
    377        if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable,fScriptTagV2,fLangSysTag,success)) {
    378           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
    379                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
    380 
    381         } else {
    382           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
    383                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
    384         }
    385     }
    386 
    387     return count;
    388 }
    389 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success)
    390 {
    391     if (LE_FAILURE(success)) {
    392         return 0;
    393     }
    394 
    395     glyphStorage.adoptGlyphArray(tempGlyphStorage);
    396     glyphStorage.adoptCharIndicesArray(tempGlyphStorage);
    397     glyphStorage.adoptAuxDataArray(tempGlyphStorage);
    398     glyphStorage.adoptGlyphCount(tempGlyphStorage);
    399 
    400     return glyphStorage.getGlyphCount();
    401 }
    402 
    403 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success)
    404 {
    405     LEUnicode *outChars = NULL;
    406     LEGlyphStorage fakeGlyphStorage;
    407     le_int32 outCharCount, outGlyphCount;
    408 
    409     if (LE_FAILURE(success)) {
    410         return 0;
    411     }
    412 
    413     if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
    414         success = LE_ILLEGAL_ARGUMENT_ERROR;
    415         return 0;
    416     }
    417 
    418     outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success);
    419 
    420     if (LE_FAILURE(success)) {
    421         return 0;
    422     }
    423 
    424     if (outChars != NULL) {
    425         // le_int32 fakeGlyphCount =
    426         glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success);
    427         LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
    428         //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
    429     } else {
    430         // le_int32 fakeGlyphCount =
    431         glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
    432         //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
    433     }
    434 
    435     if (LE_FAILURE(success)) {
    436         return 0;
    437     }
    438 
    439     outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
    440 
    441     return outGlyphCount;
    442 }
    443 
    444 // apply GPOS table, if any
    445 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
    446                                                 LEGlyphStorage &glyphStorage, LEErrorCode &success)
    447 {
    448     if (LE_FAILURE(success)) {
    449         return;
    450     }
    451 
    452     if (chars == NULL || offset < 0 || count < 0) {
    453         success = LE_ILLEGAL_ARGUMENT_ERROR;
    454         return;
    455     }
    456 
    457     le_int32 glyphCount = glyphStorage.getGlyphCount();
    458     if (glyphCount == 0) {
    459         return;
    460     }
    461 
    462     if (!fGPOSTable.isEmpty()) {
    463         GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
    464         le_int32 i;
    465 
    466         if (adjustments == NULL) {
    467             success = LE_MEMORY_ALLOCATION_ERROR;
    468             return;
    469         }
    470 
    471 #if 0
    472         // Don't need to do this if we allocate
    473         // the adjustments array w/ new...
    474         for (i = 0; i < glyphCount; i += 1) {
    475             adjustments->setXPlacement(i, 0);
    476             adjustments->setYPlacement(i, 0);
    477 
    478             adjustments->setXAdvance(i, 0);
    479             adjustments->setYAdvance(i, 0);
    480 
    481             adjustments->setBaseOffset(i, -1);
    482         }
    483 #endif
    484 
    485         if (!fGPOSTable.isEmpty()) {
    486             if (fScriptTagV2 != nullScriptTag &&
    487                 fGPOSTable->coversScriptAndLanguage(fGPOSTable, fScriptTagV2,fLangSysTag,success)) {
    488               fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag,
    489                                   fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
    490 
    491             } else {
    492               fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag,
    493                                   fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
    494             }
    495         } else if (fTypoFlags & LE_Kerning_FEATURE_FLAG) { /* kerning enabled */
    496           LETableReference kernTable(fFontInstance, LE_KERN_TABLE_TAG, success);
    497           KernTable kt(kernTable, success);
    498           kt.process(glyphStorage, success);
    499         }
    500 
    501         float xAdjust = 0, yAdjust = 0;
    502 
    503         for (i = 0; i < glyphCount; i += 1) {
    504             float xAdvance   = adjustments->getXAdvance(i);
    505             float yAdvance   = adjustments->getYAdvance(i);
    506             float xPlacement = 0;
    507             float yPlacement = 0;
    508 
    509 
    510 #if 0
    511             // This is where separate kerning adjustments
    512             // should get applied.
    513             xAdjust += xKerning;
    514             yAdjust += yKerning;
    515 #endif
    516 
    517             for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
    518                 xPlacement += adjustments->getXPlacement(base);
    519                 yPlacement += adjustments->getYPlacement(base);
    520             }
    521 
    522             xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
    523             yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
    524             glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
    525 
    526             xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
    527             yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
    528         }
    529 
    530         glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
    531 
    532         delete adjustments;
    533     } else {
    534         // if there was no GPOS table, maybe there's non-OpenType kerning we can use
    535         //   Google Patch: disable this.  Causes problems with Tamil.
    536         //       Umesh says layout is poor both with and without the change, but
    537         //       worse with the change.  See ocean/imageprocessing/layout_test_unittest.cc
    538         //   Public ICU ticket for this problem is  #7742
    539         // LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success);
    540     }
    541 
    542     LEGlyphID zwnj  = fFontInstance->mapCharToGlyph(0x200C);
    543 
    544     if (zwnj != 0x0000) {
    545         for (le_int32 g = 0; g < glyphCount; g += 1) {
    546             LEGlyphID glyph = glyphStorage[g];
    547 
    548             if (glyph == zwnj) {
    549                 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
    550             }
    551         }
    552     }
    553 
    554 #if 0
    555     // Don't know why this is here...
    556     LE_DELETE_ARRAY(fFeatureTags);
    557     fFeatureTags = NULL;
    558 #endif
    559 }
    560 
    561 U_NAMESPACE_END
    562