• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
applySubstitutionLookups(const LookupProcessor * lookupProcessor,const SubstitutionLookupRecord * substLookupRecordArray,le_uint16 substCount,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,le_int32 position,LEErrorCode & success)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 
matchGlyphIDs(const TTGlyphID * glyphArray,le_uint16 glyphCount,GlyphIterator * glyphIterator,le_bool backtrack)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 
matchGlyphClasses(const le_uint16 * classArray,le_uint16 glyphCount,GlyphIterator * glyphIterator,const ClassDefinitionTable * classDefinitionTable,le_bool backtrack)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 
matchGlyphCoverages(const Offset * coverageTableOffsetArray,le_uint16 glyphCount,GlyphIterator * glyphIterator,const char * offsetBase,le_bool backtrack)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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const148 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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const185 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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const234 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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const286 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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const319 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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const362 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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const433 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 
process(const LookupProcessor * lookupProcessor,GlyphIterator * glyphIterator,const LEFontInstance * fontInstance,LEErrorCode & success) const513 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