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