Home | History | Annotate | Download | only in layout
      1 /*
      2  * (C) Copyright IBM Corp. 1998-2013 - All Rights Reserved
      3  *
      4  */
      5 
      6 #include "LETypes.h"
      7 #include "LEFontInstance.h"
      8 #include "OpenTypeTables.h"
      9 #include "GlyphSubstitutionTables.h"
     10 #include "ContextualSubstSubtables.h"
     11 #include "GlyphIterator.h"
     12 #include "LookupProcessor.h"
     13 #include "CoverageTables.h"
     14 #include "LESwaps.h"
     15 
     16 U_NAMESPACE_BEGIN
     17 
     18 /*
     19     NOTE: This could be optimized somewhat by keeping track
     20     of the previous sequenceIndex in the loop and doing next()
     21     or prev() of the delta between that and the current
     22     sequenceIndex instead of always resetting to the front.
     23 */
     24 void ContextualSubstitutionBase::applySubstitutionLookups(
     25         const LookupProcessor *lookupProcessor,
     26         const SubstitutionLookupRecord *substLookupRecordArray,
     27         le_uint16 substCount,
     28         GlyphIterator *glyphIterator,
     29         const LEFontInstance *fontInstance,
     30         le_int32 position,
     31         LEErrorCode& success)
     32 {
     33     if (LE_FAILURE(success)) {
     34         return;
     35     }
     36 
     37     GlyphIterator tempIterator(*glyphIterator);
     38 
     39     for (le_int16 subst = 0; subst < substCount && LE_SUCCESS(success); subst += 1) {
     40         le_uint16 sequenceIndex = SWAPW(substLookupRecordArray[subst].sequenceIndex);
     41         le_uint16 lookupListIndex = SWAPW(substLookupRecordArray[subst].lookupListIndex);
     42 
     43         tempIterator.setCurrStreamPosition(position);
     44         tempIterator.next(sequenceIndex);
     45 
     46         lookupProcessor->applySingleLookup(lookupListIndex, &tempIterator, fontInstance, success);
     47     }
     48 }
     49 
     50 le_bool ContextualSubstitutionBase::matchGlyphIDs(const TTGlyphID *glyphArray, le_uint16 glyphCount,
     51                                                GlyphIterator *glyphIterator, le_bool backtrack)
     52 {
     53     le_int32 direction = 1;
     54     le_int32 match = 0;
     55 
     56     if (backtrack) {
     57         match = glyphCount -1;
     58         direction = -1;
     59     }
     60 
     61     while (glyphCount > 0) {
     62         if (! glyphIterator->next()) {
     63             return FALSE;
     64         }
     65 
     66         TTGlyphID glyph = (TTGlyphID) glyphIterator->getCurrGlyphID();
     67 
     68         if (glyph != SWAPW(glyphArray[match])) {
     69             return FALSE;
     70         }
     71 
     72         glyphCount -= 1;
     73         match += direction;
     74     }
     75 
     76     return TRUE;
     77 }
     78 
     79 le_bool ContextualSubstitutionBase::matchGlyphClasses(const le_uint16 *classArray, le_uint16 glyphCount,
     80                                                GlyphIterator *glyphIterator,
     81                                                const ClassDefinitionTable *classDefinitionTable,
     82                                                le_bool backtrack)
     83 {
     84     le_int32 direction = 1;
     85     le_int32 match = 0;
     86 
     87     if (backtrack) {
     88         match = glyphCount - 1;
     89         direction = -1;
     90     }
     91 
     92     while (glyphCount > 0) {
     93         if (! glyphIterator->next()) {
     94             return FALSE;
     95         }
     96 
     97         LEGlyphID glyph = glyphIterator->getCurrGlyphID();
     98         le_int32 glyphClass = classDefinitionTable->getGlyphClass(glyph);
     99         le_int32 matchClass = SWAPW(classArray[match]);
    100 
    101         if (glyphClass != matchClass) {
    102             // Some fonts, e.g. Traditional Arabic, have classes
    103             // in the class array which aren't in the class definition
    104             // table. If we're looking for such a class, pretend that
    105             // we found it.
    106             if (classDefinitionTable->hasGlyphClass(matchClass)) {
    107                 return FALSE;
    108             }
    109         }
    110 
    111         glyphCount -= 1;
    112         match += direction;
    113     }
    114 
    115     return TRUE;
    116 }
    117 
    118 le_bool ContextualSubstitutionBase::matchGlyphCoverages(const Offset *coverageTableOffsetArray, le_uint16 glyphCount,
    119                                                      GlyphIterator *glyphIterator, const char *offsetBase, le_bool backtrack)
    120 {
    121     le_int32 direction = 1;
    122     le_int32 glyph = 0;
    123 
    124     if (backtrack) {
    125         glyph = glyphCount - 1;
    126         direction = -1;
    127     }
    128 
    129     while (glyphCount > 0) {
    130         Offset coverageTableOffset = SWAPW(coverageTableOffsetArray[glyph]);
    131         /* Google patch: Behdad says: Unsafe dereference follows. */
    132         const CoverageTable *coverageTable = (const CoverageTable *) (offsetBase + coverageTableOffset);
    133 
    134         if (! glyphIterator->next()) {
    135             return FALSE;
    136         }
    137 
    138         if (coverageTable->getGlyphCoverage((LEGlyphID) glyphIterator->getCurrGlyphID()) < 0) {
    139             return FALSE;
    140         }
    141 
    142         glyphCount -= 1;
    143         glyph += direction;
    144     }
    145 
    146     return TRUE;
    147 }
    148 
    149 le_uint32 ContextualSubstitutionSubtable::process(const LookupProcessor *lookupProcessor,
    150                                                   GlyphIterator *glyphIterator,
    151                                                   const LEFontInstance *fontInstance,
    152                                                   LEErrorCode& success) const
    153 {
    154     if (LE_FAILURE(success)) {
    155         return 0;
    156     }
    157 
    158     switch(SWAPW(subtableFormat))
    159     {
    160     case 0:
    161         return 0;
    162 
    163     /* Google patch: Behdad says: Unsafe downcasts follow. */
    164 
    165     case 1:
    166     {
    167         const ContextualSubstitutionFormat1Subtable *subtable = (const ContextualSubstitutionFormat1Subtable *) this;
    168         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    169     }
    170 
    171     case 2:
    172     {
    173         const ContextualSubstitutionFormat2Subtable *subtable = (const ContextualSubstitutionFormat2Subtable *) this;
    174         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    175     }
    176 
    177     case 3:
    178     {
    179         const ContextualSubstitutionFormat3Subtable *subtable = (const ContextualSubstitutionFormat3Subtable *) this;
    180         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    181     }
    182 
    183     default:
    184         return 0;
    185     }
    186 }
    187 
    188 le_uint32 ContextualSubstitutionFormat1Subtable::process(const LookupProcessor *lookupProcessor,
    189                                                          GlyphIterator *glyphIterator,
    190                                                          const LEFontInstance *fontInstance,
    191                                                          LEErrorCode& success) const
    192 {
    193     if (LE_FAILURE(success)) {
    194         return 0;
    195     }
    196 
    197     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    198     le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
    199 
    200     if (coverageIndex >= 0) {
    201         le_uint16 srSetCount = SWAPW(subRuleSetCount);
    202 
    203         if (coverageIndex < srSetCount) {
    204             Offset subRuleSetTableOffset = SWAPW(subRuleSetTableOffsetArray[coverageIndex]);
    205             /* Google patch: Behdad says: Unsafe dereference follows. */
    206             const SubRuleSetTable *subRuleSetTable =
    207                 (const SubRuleSetTable *) ((char *) this + subRuleSetTableOffset);
    208             le_uint16 subRuleCount = SWAPW(subRuleSetTable->subRuleCount);
    209             le_int32 position = glyphIterator->getCurrStreamPosition();
    210 
    211             for (le_uint16 subRule = 0; subRule < subRuleCount; subRule += 1) {
    212                 Offset subRuleTableOffset =
    213                     SWAPW(subRuleSetTable->subRuleTableOffsetArray[subRule]);
    214                 const SubRuleTable *subRuleTable =
    215                     (const SubRuleTable *) ((char *) subRuleSetTable + subRuleTableOffset);
    216                 le_uint16 matchCount = SWAPW(subRuleTable->glyphCount) - 1;
    217                 le_uint16 substCount = SWAPW(subRuleTable->substCount);
    218 
    219                 if (matchGlyphIDs(subRuleTable->inputGlyphArray, matchCount, glyphIterator)) {
    220                     const SubstitutionLookupRecord *substLookupRecordArray =
    221                         (const SubstitutionLookupRecord *) &subRuleTable->inputGlyphArray[matchCount];
    222 
    223                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    224 
    225                     return matchCount + 1;
    226                 }
    227 
    228                 glyphIterator->setCurrStreamPosition(position);
    229             }
    230         }
    231 
    232         // XXX If we get here, the table is mal-formed...
    233     }
    234 
    235     return 0;
    236 }
    237 
    238 le_uint32 ContextualSubstitutionFormat2Subtable::process(const LookupProcessor *lookupProcessor,
    239                                                          GlyphIterator *glyphIterator,
    240                                                          const LEFontInstance *fontInstance,
    241                                                          LEErrorCode& success) const
    242 {
    243     if (LE_FAILURE(success)) {
    244         return 0;
    245     }
    246 
    247     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    248     le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
    249 
    250     if (coverageIndex >= 0) {
    251         /* Google patch: Behdad says: Unsafe dereference follows. */
    252         const ClassDefinitionTable *classDefinitionTable =
    253             (const ClassDefinitionTable *) ((char *) this + SWAPW(classDefTableOffset));
    254         le_uint16 scSetCount = SWAPW(subClassSetCount);
    255         le_int32 setClass = classDefinitionTable->getGlyphClass(glyphIterator->getCurrGlyphID());
    256 
    257         if (setClass < scSetCount && subClassSetTableOffsetArray[setClass] != 0) {
    258             Offset subClassSetTableOffset = SWAPW(subClassSetTableOffsetArray[setClass]);
    259             /* Google patch: Behdad says: Unsafe dereference follows. */
    260             const SubClassSetTable *subClassSetTable =
    261                 (const SubClassSetTable *) ((char *) this + subClassSetTableOffset);
    262             le_uint16 subClassRuleCount = SWAPW(subClassSetTable->subClassRuleCount);
    263             le_int32 position = glyphIterator->getCurrStreamPosition();
    264 
    265             for (le_uint16 scRule = 0; scRule < subClassRuleCount; scRule += 1) {
    266                 Offset subClassRuleTableOffset =
    267                     SWAPW(subClassSetTable->subClassRuleTableOffsetArray[scRule]);
    268                 const SubClassRuleTable *subClassRuleTable =
    269                     (const SubClassRuleTable *) ((char *) subClassSetTable + subClassRuleTableOffset);
    270                 le_uint16 matchCount = SWAPW(subClassRuleTable->glyphCount) - 1;
    271                 le_uint16 substCount = SWAPW(subClassRuleTable->substCount);
    272 
    273                 if (matchGlyphClasses(subClassRuleTable->classArray, matchCount, glyphIterator, classDefinitionTable)) {
    274                     const SubstitutionLookupRecord *substLookupRecordArray =
    275                         (const SubstitutionLookupRecord *) &subClassRuleTable->classArray[matchCount];
    276 
    277                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    278 
    279                     return matchCount + 1;
    280                 }
    281 
    282                 glyphIterator->setCurrStreamPosition(position);
    283             }
    284         }
    285 
    286         // XXX If we get here, the table is mal-formed...
    287     }
    288 
    289     return 0;
    290 }
    291 
    292 le_uint32 ContextualSubstitutionFormat3Subtable::process(const LookupProcessor *lookupProcessor,
    293                                                          GlyphIterator *glyphIterator,
    294                                                          const LEFontInstance *fontInstance,
    295                                                          LEErrorCode& success)const
    296 {
    297     if (LE_FAILURE(success)) {
    298         return 0;
    299     }
    300 
    301     le_uint16 gCount = SWAPW(glyphCount);
    302     le_uint16 subCount = SWAPW(substCount);
    303     le_int32 position = glyphIterator->getCurrStreamPosition();
    304 
    305     // Back up the glyph iterator so that we
    306     // can call next() before the check, which
    307     // will leave it pointing at the last glyph
    308     // that matched when we're done.
    309     glyphIterator->prev();
    310 
    311     if (ContextualSubstitutionBase::matchGlyphCoverages(coverageTableOffsetArray, gCount, glyphIterator, (const char *) this)) {
    312         const SubstitutionLookupRecord *substLookupRecordArray =
    313             (const SubstitutionLookupRecord *) &coverageTableOffsetArray[gCount];
    314 
    315         ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, subCount, glyphIterator, fontInstance, position, success);
    316 
    317         return gCount + 1;
    318     }
    319 
    320     glyphIterator->setCurrStreamPosition(position);
    321 
    322     return 0;
    323 }
    324 
    325 le_uint32 ChainingContextualSubstitutionSubtable::process(const LookupProcessor *lookupProcessor,
    326                                                           GlyphIterator *glyphIterator,
    327                                                           const LEFontInstance *fontInstance,
    328                                                           LEErrorCode& success) const
    329 {
    330     if (LE_FAILURE(success)) {
    331         return 0;
    332     }
    333 
    334     switch(SWAPW(subtableFormat))
    335     {
    336     case 0:
    337         return 0;
    338 
    339     /* Google patch: Behdad says: Unsafe downcasts follow. */
    340 
    341     case 1:
    342     {
    343         const ChainingContextualSubstitutionFormat1Subtable *subtable = (const ChainingContextualSubstitutionFormat1Subtable *) this;
    344         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    345     }
    346 
    347     case 2:
    348     {
    349         const ChainingContextualSubstitutionFormat2Subtable *subtable = (const ChainingContextualSubstitutionFormat2Subtable *) this;
    350         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    351     }
    352 
    353     case 3:
    354     {
    355         const ChainingContextualSubstitutionFormat3Subtable *subtable = (const ChainingContextualSubstitutionFormat3Subtable *) this;
    356         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    357     }
    358 
    359     default:
    360         return 0;
    361     }
    362 }
    363 
    364 // NOTE: This could be a #define, but that seems to confuse
    365 // the Visual Studio .NET 2003 compiler on the calls to the
    366 // GlyphIterator constructor. It somehow can't decide if
    367 // emptyFeatureList matches an le_uint32 or an le_uint16...
    368 static const FeatureMask emptyFeatureList = 0x00000000UL;
    369 
    370 le_uint32 ChainingContextualSubstitutionFormat1Subtable::process(const LookupProcessor *lookupProcessor,
    371                                                                  GlyphIterator *glyphIterator,
    372                                                                  const LEFontInstance *fontInstance,
    373                                                                  LEErrorCode& success) const
    374 {
    375     if (LE_FAILURE(success)) {
    376         return 0;
    377     }
    378 
    379     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    380     le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
    381 
    382     if (coverageIndex >= 0) {
    383         le_uint16 srSetCount = SWAPW(chainSubRuleSetCount);
    384 
    385         if (coverageIndex < srSetCount) {
    386             Offset chainSubRuleSetTableOffset = SWAPW(chainSubRuleSetTableOffsetArray[coverageIndex]);
    387             /* Google patch: Behdad says: Unsafe dereference follows. */
    388             const ChainSubRuleSetTable *chainSubRuleSetTable =
    389                 (const ChainSubRuleSetTable *) ((char *) this + chainSubRuleSetTableOffset);
    390             le_uint16 chainSubRuleCount = SWAPW(chainSubRuleSetTable->chainSubRuleCount);
    391             le_int32 position = glyphIterator->getCurrStreamPosition();
    392             GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
    393 
    394             for (le_uint16 subRule = 0; subRule < chainSubRuleCount; subRule += 1) {
    395                 Offset chainSubRuleTableOffset =
    396                     SWAPW(chainSubRuleSetTable->chainSubRuleTableOffsetArray[subRule]);
    397                 const ChainSubRuleTable *chainSubRuleTable =
    398                     (const ChainSubRuleTable *) ((char *) chainSubRuleSetTable + chainSubRuleTableOffset);
    399                 le_uint16 backtrackGlyphCount = SWAPW(chainSubRuleTable->backtrackGlyphCount);
    400                 le_uint16 inputGlyphCount = (le_uint16) SWAPW(chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount]) - 1;
    401                 const TTGlyphID *inputGlyphArray = &chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount + 1];
    402                 le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputGlyphArray[inputGlyphCount]);
    403                 const TTGlyphID *lookaheadGlyphArray = &inputGlyphArray[inputGlyphCount + 1];
    404                 le_uint16 substCount = (le_uint16) SWAPW(lookaheadGlyphArray[lookaheadGlyphCount]);
    405 
    406                 tempIterator.setCurrStreamPosition(position);
    407 
    408                 if (! tempIterator.prev(backtrackGlyphCount)) {
    409                     continue;
    410                 }
    411 
    412                 tempIterator.prev();
    413                 if (! matchGlyphIDs(chainSubRuleTable->backtrackGlyphArray, backtrackGlyphCount, &tempIterator, TRUE)) {
    414                     continue;
    415                 }
    416 
    417                 tempIterator.setCurrStreamPosition(position);
    418                 tempIterator.next(inputGlyphCount);
    419                 if (!matchGlyphIDs(lookaheadGlyphArray, lookaheadGlyphCount, &tempIterator)) {
    420                     continue;
    421                 }
    422 
    423                 if (matchGlyphIDs(inputGlyphArray, inputGlyphCount, glyphIterator)) {
    424                     const SubstitutionLookupRecord *substLookupRecordArray =
    425                         (const SubstitutionLookupRecord *) &lookaheadGlyphArray[lookaheadGlyphCount + 1];
    426 
    427                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    428 
    429                     return inputGlyphCount + 1;
    430                 }
    431 
    432                 glyphIterator->setCurrStreamPosition(position);
    433             }
    434         }
    435 
    436         // XXX If we get here, the table is mal-formed...
    437     }
    438 
    439     return 0;
    440 }
    441 
    442 le_uint32 ChainingContextualSubstitutionFormat2Subtable::process(const LookupProcessor *lookupProcessor,
    443                                                                  GlyphIterator *glyphIterator,
    444                                                                  const LEFontInstance *fontInstance,
    445                                                                  LEErrorCode& success) const
    446 {
    447     if (LE_FAILURE(success)) {
    448         return 0;
    449     }
    450 
    451     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    452     le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
    453 
    454     if (coverageIndex >= 0) {
    455         /* Google patch: Behdad says: Unsafe dereferences follow. */
    456         const ClassDefinitionTable *backtrackClassDefinitionTable =
    457             (const ClassDefinitionTable *) ((char *) this + SWAPW(backtrackClassDefTableOffset));
    458         const ClassDefinitionTable *inputClassDefinitionTable =
    459             (const ClassDefinitionTable *) ((char *) this + SWAPW(inputClassDefTableOffset));
    460         const ClassDefinitionTable *lookaheadClassDefinitionTable =
    461             (const ClassDefinitionTable *) ((char *) this + SWAPW(lookaheadClassDefTableOffset));
    462         le_uint16 scSetCount = SWAPW(chainSubClassSetCount);
    463         le_int32 setClass = inputClassDefinitionTable->getGlyphClass(glyphIterator->getCurrGlyphID());
    464 
    465         if (setClass < scSetCount && chainSubClassSetTableOffsetArray[setClass] != 0) {
    466             Offset chainSubClassSetTableOffset = SWAPW(chainSubClassSetTableOffsetArray[setClass]);
    467             /* Google patch: Behdad says: Unsafe dereference follows. */
    468             const ChainSubClassSetTable *chainSubClassSetTable =
    469                 (const ChainSubClassSetTable *) ((char *) this + chainSubClassSetTableOffset);
    470             le_uint16 chainSubClassRuleCount = SWAPW(chainSubClassSetTable->chainSubClassRuleCount);
    471             le_int32 position = glyphIterator->getCurrStreamPosition();
    472             GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
    473 
    474             for (le_uint16 scRule = 0; scRule < chainSubClassRuleCount; scRule += 1) {
    475                 Offset chainSubClassRuleTableOffset =
    476                     SWAPW(chainSubClassSetTable->chainSubClassRuleTableOffsetArray[scRule]);
    477                 const ChainSubClassRuleTable *chainSubClassRuleTable =
    478                     (const ChainSubClassRuleTable *) ((char *) chainSubClassSetTable + chainSubClassRuleTableOffset);
    479                 le_uint16 backtrackGlyphCount = SWAPW(chainSubClassRuleTable->backtrackGlyphCount);
    480                 le_uint16 inputGlyphCount = SWAPW(chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount]) - 1;
    481                 const le_uint16 *inputClassArray = &chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount + 1];
    482                 le_uint16 lookaheadGlyphCount = SWAPW(inputClassArray[inputGlyphCount]);
    483                 const le_uint16 *lookaheadClassArray = &inputClassArray[inputGlyphCount + 1];
    484                 le_uint16 substCount = SWAPW(lookaheadClassArray[lookaheadGlyphCount]);
    485 
    486 
    487                 tempIterator.setCurrStreamPosition(position);
    488 
    489                 if (! tempIterator.prev(backtrackGlyphCount)) {
    490                     continue;
    491                 }
    492 
    493                 tempIterator.prev();
    494                 if (! matchGlyphClasses(chainSubClassRuleTable->backtrackClassArray, backtrackGlyphCount,
    495                     &tempIterator, backtrackClassDefinitionTable, TRUE)) {
    496                     continue;
    497                 }
    498 
    499                 tempIterator.setCurrStreamPosition(position);
    500                 tempIterator.next(inputGlyphCount);
    501                 if (! matchGlyphClasses(lookaheadClassArray, lookaheadGlyphCount, &tempIterator, lookaheadClassDefinitionTable)) {
    502                     continue;
    503                 }
    504 
    505                 if (matchGlyphClasses(inputClassArray, inputGlyphCount, glyphIterator, inputClassDefinitionTable)) {
    506                     const SubstitutionLookupRecord *substLookupRecordArray =
    507                         (const SubstitutionLookupRecord *) &lookaheadClassArray[lookaheadGlyphCount + 1];
    508 
    509                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    510 
    511                     return inputGlyphCount + 1;
    512                 }
    513 
    514                 glyphIterator->setCurrStreamPosition(position);
    515             }
    516         }
    517 
    518         // XXX If we get here, the table is mal-formed...
    519     }
    520 
    521     return 0;
    522 }
    523 
    524 le_uint32 ChainingContextualSubstitutionFormat3Subtable::process(const LookupProcessor *lookupProcessor,
    525                                                                  GlyphIterator *glyphIterator,
    526                                                                  const LEFontInstance *fontInstance,
    527                                                                  LEErrorCode & success) const
    528 {
    529     if (LE_FAILURE(success)) {
    530         return 0;
    531     }
    532 
    533     le_uint16 backtrkGlyphCount = SWAPW(backtrackGlyphCount);
    534     le_uint16 inputGlyphCount = (le_uint16) SWAPW(backtrackCoverageTableOffsetArray[backtrkGlyphCount]);
    535     const Offset *inputCoverageTableOffsetArray = &backtrackCoverageTableOffsetArray[backtrkGlyphCount + 1];
    536     const le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputCoverageTableOffsetArray[inputGlyphCount]);
    537     const Offset *lookaheadCoverageTableOffsetArray = &inputCoverageTableOffsetArray[inputGlyphCount + 1];
    538     le_uint16 substCount = (le_uint16) SWAPW(lookaheadCoverageTableOffsetArray[lookaheadGlyphCount]);
    539     le_int32 position = glyphIterator->getCurrStreamPosition();
    540     GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
    541 
    542     if (! tempIterator.prev(backtrkGlyphCount)) {
    543         return 0;
    544     }
    545 
    546     tempIterator.prev();
    547     if (! ContextualSubstitutionBase::matchGlyphCoverages(backtrackCoverageTableOffsetArray,
    548         backtrkGlyphCount, &tempIterator, (const char *) this, TRUE)) {
    549         return 0;
    550     }
    551 
    552     tempIterator.setCurrStreamPosition(position);
    553     tempIterator.next(inputGlyphCount - 1);
    554     if (! ContextualSubstitutionBase::matchGlyphCoverages(lookaheadCoverageTableOffsetArray,
    555         lookaheadGlyphCount, &tempIterator, (const char *) this)) {
    556         return 0;
    557     }
    558 
    559     // Back up the glyph iterator so that we
    560     // can call next() before the check, which
    561     // will leave it pointing at the last glyph
    562     // that matched when we're done.
    563     glyphIterator->prev();
    564 
    565     if (ContextualSubstitutionBase::matchGlyphCoverages(inputCoverageTableOffsetArray,
    566         inputGlyphCount, glyphIterator, (const char *) this)) {
    567         const SubstitutionLookupRecord *substLookupRecordArray =
    568             (const SubstitutionLookupRecord *) &lookaheadCoverageTableOffsetArray[lookaheadGlyphCount + 1];
    569 
    570         ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    571 
    572         return inputGlyphCount;
    573     }
    574 
    575     glyphIterator->setCurrStreamPosition(position);
    576 
    577     return 0;
    578 }
    579 
    580 U_NAMESPACE_END
    581