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