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