Home | History | Annotate | Download | only in layout
      1 /*
      2  * (C) Copyright IBM Corp. 1998-2008 - 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         const CoverageTable *coverageTable = (const CoverageTable *) (offsetBase + coverageTableOffset);
    132 
    133         if (! glyphIterator->next()) {
    134             return FALSE;
    135         }
    136 
    137         if (coverageTable->getGlyphCoverage((LEGlyphID) glyphIterator->getCurrGlyphID()) < 0) {
    138             return FALSE;
    139         }
    140 
    141         glyphCount -= 1;
    142         glyph += direction;
    143     }
    144 
    145     return TRUE;
    146 }
    147 
    148 le_uint32 ContextualSubstitutionSubtable::process(const LookupProcessor *lookupProcessor,
    149                                                   GlyphIterator *glyphIterator,
    150                                                   const LEFontInstance *fontInstance,
    151                                                   LEErrorCode& success) const
    152 {
    153     if (LE_FAILURE(success)) {
    154         return 0;
    155     }
    156 
    157     switch(SWAPW(subtableFormat))
    158     {
    159     case 0:
    160         return 0;
    161 
    162     case 1:
    163     {
    164         const ContextualSubstitutionFormat1Subtable *subtable = (const ContextualSubstitutionFormat1Subtable *) this;
    165         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    166     }
    167 
    168     case 2:
    169     {
    170         const ContextualSubstitutionFormat2Subtable *subtable = (const ContextualSubstitutionFormat2Subtable *) this;
    171         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    172     }
    173 
    174     case 3:
    175     {
    176         const ContextualSubstitutionFormat3Subtable *subtable = (const ContextualSubstitutionFormat3Subtable *) this;
    177         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    178     }
    179 
    180     default:
    181         return 0;
    182     }
    183 }
    184 
    185 le_uint32 ContextualSubstitutionFormat1Subtable::process(const LookupProcessor *lookupProcessor,
    186                                                          GlyphIterator *glyphIterator,
    187                                                          const LEFontInstance *fontInstance,
    188                                                          LEErrorCode& success) const
    189 {
    190     if (LE_FAILURE(success)) {
    191         return 0;
    192     }
    193 
    194     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    195     le_int32 coverageIndex = getGlyphCoverage(glyph);
    196 
    197     if (coverageIndex >= 0) {
    198         le_uint16 srSetCount = SWAPW(subRuleSetCount);
    199 
    200         if (coverageIndex < srSetCount) {
    201             Offset subRuleSetTableOffset = SWAPW(subRuleSetTableOffsetArray[coverageIndex]);
    202             const SubRuleSetTable *subRuleSetTable =
    203                 (const SubRuleSetTable *) ((char *) this + subRuleSetTableOffset);
    204             le_uint16 subRuleCount = SWAPW(subRuleSetTable->subRuleCount);
    205             le_int32 position = glyphIterator->getCurrStreamPosition();
    206 
    207             for (le_uint16 subRule = 0; subRule < subRuleCount; subRule += 1) {
    208                 Offset subRuleTableOffset =
    209                     SWAPW(subRuleSetTable->subRuleTableOffsetArray[subRule]);
    210                 const SubRuleTable *subRuleTable =
    211                     (const SubRuleTable *) ((char *) subRuleSetTable + subRuleTableOffset);
    212                 le_uint16 matchCount = SWAPW(subRuleTable->glyphCount) - 1;
    213                 le_uint16 substCount = SWAPW(subRuleTable->substCount);
    214 
    215                 if (matchGlyphIDs(subRuleTable->inputGlyphArray, matchCount, glyphIterator)) {
    216                     const SubstitutionLookupRecord *substLookupRecordArray =
    217                         (const SubstitutionLookupRecord *) &subRuleTable->inputGlyphArray[matchCount];
    218 
    219                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    220 
    221                     return matchCount + 1;
    222                 }
    223 
    224                 glyphIterator->setCurrStreamPosition(position);
    225             }
    226         }
    227 
    228         // XXX If we get here, the table is mal-formed...
    229     }
    230 
    231     return 0;
    232 }
    233 
    234 le_uint32 ContextualSubstitutionFormat2Subtable::process(const LookupProcessor *lookupProcessor,
    235                                                          GlyphIterator *glyphIterator,
    236                                                          const LEFontInstance *fontInstance,
    237                                                          LEErrorCode& success) const
    238 {
    239     if (LE_FAILURE(success)) {
    240         return 0;
    241     }
    242 
    243     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    244     le_int32 coverageIndex = getGlyphCoverage(glyph);
    245 
    246     if (coverageIndex >= 0) {
    247         const ClassDefinitionTable *classDefinitionTable =
    248             (const ClassDefinitionTable *) ((char *) this + SWAPW(classDefTableOffset));
    249         le_uint16 scSetCount = SWAPW(subClassSetCount);
    250         le_int32 setClass = classDefinitionTable->getGlyphClass(glyphIterator->getCurrGlyphID());
    251 
    252         if (setClass < scSetCount && subClassSetTableOffsetArray[setClass] != 0) {
    253             Offset subClassSetTableOffset = SWAPW(subClassSetTableOffsetArray[setClass]);
    254             const SubClassSetTable *subClassSetTable =
    255                 (const SubClassSetTable *) ((char *) this + subClassSetTableOffset);
    256             le_uint16 subClassRuleCount = SWAPW(subClassSetTable->subClassRuleCount);
    257             le_int32 position = glyphIterator->getCurrStreamPosition();
    258 
    259             for (le_uint16 scRule = 0; scRule < subClassRuleCount; scRule += 1) {
    260                 Offset subClassRuleTableOffset =
    261                     SWAPW(subClassSetTable->subClassRuleTableOffsetArray[scRule]);
    262                 const SubClassRuleTable *subClassRuleTable =
    263                     (const SubClassRuleTable *) ((char *) subClassSetTable + subClassRuleTableOffset);
    264                 le_uint16 matchCount = SWAPW(subClassRuleTable->glyphCount) - 1;
    265                 le_uint16 substCount = SWAPW(subClassRuleTable->substCount);
    266 
    267                 if (matchGlyphClasses(subClassRuleTable->classArray, matchCount, glyphIterator, classDefinitionTable)) {
    268                     const SubstitutionLookupRecord *substLookupRecordArray =
    269                         (const SubstitutionLookupRecord *) &subClassRuleTable->classArray[matchCount];
    270 
    271                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    272 
    273                     return matchCount + 1;
    274                 }
    275 
    276                 glyphIterator->setCurrStreamPosition(position);
    277             }
    278         }
    279 
    280         // XXX If we get here, the table is mal-formed...
    281     }
    282 
    283     return 0;
    284 }
    285 
    286 le_uint32 ContextualSubstitutionFormat3Subtable::process(const LookupProcessor *lookupProcessor,
    287                                                          GlyphIterator *glyphIterator,
    288                                                          const LEFontInstance *fontInstance,
    289                                                          LEErrorCode& success)const
    290 {
    291     if (LE_FAILURE(success)) {
    292         return 0;
    293     }
    294 
    295     le_uint16 gCount = SWAPW(glyphCount);
    296     le_uint16 subCount = SWAPW(substCount);
    297     le_int32 position = glyphIterator->getCurrStreamPosition();
    298 
    299     // Back up the glyph iterator so that we
    300     // can call next() before the check, which
    301     // will leave it pointing at the last glyph
    302     // that matched when we're done.
    303     glyphIterator->prev();
    304 
    305     if (ContextualSubstitutionBase::matchGlyphCoverages(coverageTableOffsetArray, gCount, glyphIterator, (const char *) this)) {
    306         const SubstitutionLookupRecord *substLookupRecordArray =
    307             (const SubstitutionLookupRecord *) &coverageTableOffsetArray[gCount];
    308 
    309         ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, subCount, glyphIterator, fontInstance, position, success);
    310 
    311         return gCount + 1;
    312     }
    313 
    314     glyphIterator->setCurrStreamPosition(position);
    315 
    316     return 0;
    317 }
    318 
    319 le_uint32 ChainingContextualSubstitutionSubtable::process(const LookupProcessor *lookupProcessor,
    320                                                           GlyphIterator *glyphIterator,
    321                                                           const LEFontInstance *fontInstance,
    322                                                           LEErrorCode& success) const
    323 {
    324     if (LE_FAILURE(success)) {
    325         return 0;
    326     }
    327 
    328     switch(SWAPW(subtableFormat))
    329     {
    330     case 0:
    331         return 0;
    332 
    333     case 1:
    334     {
    335         const ChainingContextualSubstitutionFormat1Subtable *subtable = (const ChainingContextualSubstitutionFormat1Subtable *) this;
    336         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    337     }
    338 
    339     case 2:
    340     {
    341         const ChainingContextualSubstitutionFormat2Subtable *subtable = (const ChainingContextualSubstitutionFormat2Subtable *) this;
    342         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    343     }
    344 
    345     case 3:
    346     {
    347         const ChainingContextualSubstitutionFormat3Subtable *subtable = (const ChainingContextualSubstitutionFormat3Subtable *) this;
    348         return subtable->process(lookupProcessor, glyphIterator, fontInstance, success);
    349     }
    350 
    351     default:
    352         return 0;
    353     }
    354 }
    355 
    356 // NOTE: This could be a #define, but that seems to confuse
    357 // the Visual Studio .NET 2003 compiler on the calls to the
    358 // GlyphIterator constructor. It somehow can't decide if
    359 // emptyFeatureList matches an le_uint32 or an le_uint16...
    360 static const FeatureMask emptyFeatureList = 0x00000000UL;
    361 
    362 le_uint32 ChainingContextualSubstitutionFormat1Subtable::process(const LookupProcessor *lookupProcessor,
    363                                                                  GlyphIterator *glyphIterator,
    364                                                                  const LEFontInstance *fontInstance,
    365                                                                  LEErrorCode& success) const
    366 {
    367     if (LE_FAILURE(success)) {
    368         return 0;
    369     }
    370 
    371     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    372     le_int32 coverageIndex = getGlyphCoverage(glyph);
    373 
    374     if (coverageIndex >= 0) {
    375         le_uint16 srSetCount = SWAPW(chainSubRuleSetCount);
    376 
    377         if (coverageIndex < srSetCount) {
    378             Offset chainSubRuleSetTableOffset = SWAPW(chainSubRuleSetTableOffsetArray[coverageIndex]);
    379             const ChainSubRuleSetTable *chainSubRuleSetTable =
    380                 (const ChainSubRuleSetTable *) ((char *) this + chainSubRuleSetTableOffset);
    381             le_uint16 chainSubRuleCount = SWAPW(chainSubRuleSetTable->chainSubRuleCount);
    382             le_int32 position = glyphIterator->getCurrStreamPosition();
    383             GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
    384 
    385             for (le_uint16 subRule = 0; subRule < chainSubRuleCount; subRule += 1) {
    386                 Offset chainSubRuleTableOffset =
    387                     SWAPW(chainSubRuleSetTable->chainSubRuleTableOffsetArray[subRule]);
    388                 const ChainSubRuleTable *chainSubRuleTable =
    389                     (const ChainSubRuleTable *) ((char *) chainSubRuleSetTable + chainSubRuleTableOffset);
    390                 le_uint16 backtrackGlyphCount = SWAPW(chainSubRuleTable->backtrackGlyphCount);
    391                 le_uint16 inputGlyphCount = (le_uint16) SWAPW(chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount]) - 1;
    392                 const TTGlyphID *inputGlyphArray = &chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount + 1];
    393                 le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputGlyphArray[inputGlyphCount]);
    394                 const TTGlyphID *lookaheadGlyphArray = &inputGlyphArray[inputGlyphCount + 1];
    395                 le_uint16 substCount = (le_uint16) SWAPW(lookaheadGlyphArray[lookaheadGlyphCount]);
    396 
    397                 tempIterator.setCurrStreamPosition(position);
    398 
    399                 if (! tempIterator.prev(backtrackGlyphCount)) {
    400                     continue;
    401                 }
    402 
    403                 tempIterator.prev();
    404                 if (! matchGlyphIDs(chainSubRuleTable->backtrackGlyphArray, backtrackGlyphCount, &tempIterator, TRUE)) {
    405                     continue;
    406                 }
    407 
    408                 tempIterator.setCurrStreamPosition(position);
    409                 tempIterator.next(inputGlyphCount);
    410                 if (!matchGlyphIDs(lookaheadGlyphArray, lookaheadGlyphCount, &tempIterator)) {
    411                     continue;
    412                 }
    413 
    414                 if (matchGlyphIDs(inputGlyphArray, inputGlyphCount, glyphIterator)) {
    415                     const SubstitutionLookupRecord *substLookupRecordArray =
    416                         (const SubstitutionLookupRecord *) &lookaheadGlyphArray[lookaheadGlyphCount + 1];
    417 
    418                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    419 
    420                     return inputGlyphCount + 1;
    421                 }
    422 
    423                 glyphIterator->setCurrStreamPosition(position);
    424             }
    425         }
    426 
    427         // XXX If we get here, the table is mal-formed...
    428     }
    429 
    430     return 0;
    431 }
    432 
    433 le_uint32 ChainingContextualSubstitutionFormat2Subtable::process(const LookupProcessor *lookupProcessor,
    434                                                                  GlyphIterator *glyphIterator,
    435                                                                  const LEFontInstance *fontInstance,
    436                                                                  LEErrorCode& success) const
    437 {
    438     if (LE_FAILURE(success)) {
    439         return 0;
    440     }
    441 
    442     LEGlyphID glyph = glyphIterator->getCurrGlyphID();
    443     le_int32 coverageIndex = getGlyphCoverage(glyph);
    444 
    445     if (coverageIndex >= 0) {
    446         const ClassDefinitionTable *backtrackClassDefinitionTable =
    447             (const ClassDefinitionTable *) ((char *) this + SWAPW(backtrackClassDefTableOffset));
    448         const ClassDefinitionTable *inputClassDefinitionTable =
    449             (const ClassDefinitionTable *) ((char *) this + SWAPW(inputClassDefTableOffset));
    450         const ClassDefinitionTable *lookaheadClassDefinitionTable =
    451             (const ClassDefinitionTable *) ((char *) this + SWAPW(lookaheadClassDefTableOffset));
    452         le_uint16 scSetCount = SWAPW(chainSubClassSetCount);
    453         le_int32 setClass = inputClassDefinitionTable->getGlyphClass(glyphIterator->getCurrGlyphID());
    454 
    455         if (setClass < scSetCount && chainSubClassSetTableOffsetArray[setClass] != 0) {
    456             Offset chainSubClassSetTableOffset = SWAPW(chainSubClassSetTableOffsetArray[setClass]);
    457             const ChainSubClassSetTable *chainSubClassSetTable =
    458                 (const ChainSubClassSetTable *) ((char *) this + chainSubClassSetTableOffset);
    459             le_uint16 chainSubClassRuleCount = SWAPW(chainSubClassSetTable->chainSubClassRuleCount);
    460             le_int32 position = glyphIterator->getCurrStreamPosition();
    461             GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
    462 
    463             for (le_uint16 scRule = 0; scRule < chainSubClassRuleCount; scRule += 1) {
    464                 Offset chainSubClassRuleTableOffset =
    465                     SWAPW(chainSubClassSetTable->chainSubClassRuleTableOffsetArray[scRule]);
    466                 const ChainSubClassRuleTable *chainSubClassRuleTable =
    467                     (const ChainSubClassRuleTable *) ((char *) chainSubClassSetTable + chainSubClassRuleTableOffset);
    468                 le_uint16 backtrackGlyphCount = SWAPW(chainSubClassRuleTable->backtrackGlyphCount);
    469                 le_uint16 inputGlyphCount = SWAPW(chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount]) - 1;
    470                 const le_uint16 *inputClassArray = &chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount + 1];
    471                 le_uint16 lookaheadGlyphCount = SWAPW(inputClassArray[inputGlyphCount]);
    472                 const le_uint16 *lookaheadClassArray = &inputClassArray[inputGlyphCount + 1];
    473                 le_uint16 substCount = SWAPW(lookaheadClassArray[lookaheadGlyphCount]);
    474 
    475 
    476                 tempIterator.setCurrStreamPosition(position);
    477 
    478                 if (! tempIterator.prev(backtrackGlyphCount)) {
    479                     continue;
    480                 }
    481 
    482                 tempIterator.prev();
    483                 if (! matchGlyphClasses(chainSubClassRuleTable->backtrackClassArray, backtrackGlyphCount,
    484                     &tempIterator, backtrackClassDefinitionTable, TRUE)) {
    485                     continue;
    486                 }
    487 
    488                 tempIterator.setCurrStreamPosition(position);
    489                 tempIterator.next(inputGlyphCount);
    490                 if (! matchGlyphClasses(lookaheadClassArray, lookaheadGlyphCount, &tempIterator, lookaheadClassDefinitionTable)) {
    491                     continue;
    492                 }
    493 
    494                 if (matchGlyphClasses(inputClassArray, inputGlyphCount, glyphIterator, inputClassDefinitionTable)) {
    495                     const SubstitutionLookupRecord *substLookupRecordArray =
    496                         (const SubstitutionLookupRecord *) &lookaheadClassArray[lookaheadGlyphCount + 1];
    497 
    498                     applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    499 
    500                     return inputGlyphCount + 1;
    501                 }
    502 
    503                 glyphIterator->setCurrStreamPosition(position);
    504             }
    505         }
    506 
    507         // XXX If we get here, the table is mal-formed...
    508     }
    509 
    510     return 0;
    511 }
    512 
    513 le_uint32 ChainingContextualSubstitutionFormat3Subtable::process(const LookupProcessor *lookupProcessor,
    514                                                                  GlyphIterator *glyphIterator,
    515                                                                  const LEFontInstance *fontInstance,
    516                                                                  LEErrorCode & success) const
    517 {
    518     if (LE_FAILURE(success)) {
    519         return 0;
    520     }
    521 
    522     le_uint16 backtrkGlyphCount = SWAPW(backtrackGlyphCount);
    523     le_uint16 inputGlyphCount = (le_uint16) SWAPW(backtrackCoverageTableOffsetArray[backtrkGlyphCount]);
    524     const Offset *inputCoverageTableOffsetArray = &backtrackCoverageTableOffsetArray[backtrkGlyphCount + 1];
    525     const le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputCoverageTableOffsetArray[inputGlyphCount]);
    526     const Offset *lookaheadCoverageTableOffsetArray = &inputCoverageTableOffsetArray[inputGlyphCount + 1];
    527     le_uint16 substCount = (le_uint16) SWAPW(lookaheadCoverageTableOffsetArray[lookaheadGlyphCount]);
    528     le_int32 position = glyphIterator->getCurrStreamPosition();
    529     GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
    530 
    531     if (! tempIterator.prev(backtrkGlyphCount)) {
    532         return 0;
    533     }
    534 
    535     tempIterator.prev();
    536     if (! ContextualSubstitutionBase::matchGlyphCoverages(backtrackCoverageTableOffsetArray,
    537         backtrkGlyphCount, &tempIterator, (const char *) this, TRUE)) {
    538         return 0;
    539     }
    540 
    541     tempIterator.setCurrStreamPosition(position);
    542     tempIterator.next(inputGlyphCount - 1);
    543     if (! ContextualSubstitutionBase::matchGlyphCoverages(lookaheadCoverageTableOffsetArray,
    544         lookaheadGlyphCount, &tempIterator, (const char *) this)) {
    545         return 0;
    546     }
    547 
    548     // Back up the glyph iterator so that we
    549     // can call next() before the check, which
    550     // will leave it pointing at the last glyph
    551     // that matched when we're done.
    552     glyphIterator->prev();
    553 
    554     if (ContextualSubstitutionBase::matchGlyphCoverages(inputCoverageTableOffsetArray,
    555         inputGlyphCount, glyphIterator, (const char *) this)) {
    556         const SubstitutionLookupRecord *substLookupRecordArray =
    557             (const SubstitutionLookupRecord *) &lookaheadCoverageTableOffsetArray[lookaheadGlyphCount + 1];
    558 
    559         ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
    560 
    561         return inputGlyphCount;
    562     }
    563 
    564     glyphIterator->setCurrStreamPosition(position);
    565 
    566     return 0;
    567 }
    568 
    569 U_NAMESPACE_END
    570