ContextualSubstSubtables.cpp revision 11281:e6d938af3941
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 * (C) Copyright IBM Corp. 1998-2005 - All Rights Reserved
28 *
29 */
30
31#include "LETypes.h"
32#include "LEFontInstance.h"
33#include "OpenTypeTables.h"
34#include "GlyphSubstitutionTables.h"
35#include "ContextualSubstSubtables.h"
36#include "GlyphIterator.h"
37#include "LookupProcessor.h"
38#include "CoverageTables.h"
39#include "LESwaps.h"
40
41U_NAMESPACE_BEGIN
42
43/*
44    NOTE: This could be optimized somewhat by keeping track
45    of the previous sequenceIndex in the loop and doing next()
46    or prev() of the delta between that and the current
47    sequenceIndex instead of always resetting to the front.
48*/
49void ContextualSubstitutionBase::applySubstitutionLookups(
50        const LookupProcessor *lookupProcessor,
51        const LEReferenceToArrayOf<SubstitutionLookupRecord>& substLookupRecordArray,
52        le_uint16 substCount,
53        GlyphIterator *glyphIterator,
54        const LEFontInstance *fontInstance,
55        le_int32 position,
56        LEErrorCode& success)
57{
58    if (LE_FAILURE(success)) {
59        return;
60    }
61
62    GlyphIterator tempIterator(*glyphIterator);
63    const SubstitutionLookupRecord *substLookupRecordArrayPtr = substLookupRecordArray.getAlias(); // OK to dereference, range checked against substCount below.
64
65    for (le_int16 subst = 0; subst < substCount && LE_SUCCESS(success); subst += 1) {
66        le_uint16 sequenceIndex = SWAPW(substLookupRecordArrayPtr[subst].sequenceIndex);
67        le_uint16 lookupListIndex = SWAPW(substLookupRecordArrayPtr[subst].lookupListIndex);
68
69        tempIterator.setCurrStreamPosition(position);
70        tempIterator.next(sequenceIndex);
71
72        lookupProcessor->applySingleLookup(lookupListIndex, &tempIterator, fontInstance, success);
73    }
74}
75
76le_bool ContextualSubstitutionBase::matchGlyphIDs(const LEReferenceToArrayOf<TTGlyphID>& glyphArray, le_uint16 glyphCount,
77                                               GlyphIterator *glyphIterator, 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        TTGlyphID glyph = (TTGlyphID) glyphIterator->getCurrGlyphID();
93
94        if (glyph != SWAPW(glyphArray[match])) {
95            return FALSE;
96        }
97
98        glyphCount -= 1;
99        match += direction;
100    }
101
102    return TRUE;
103}
104
105le_bool ContextualSubstitutionBase::matchGlyphClasses(
106    const LEReferenceToArrayOf<le_uint16> &classArray,
107    le_uint16 glyphCount,
108    GlyphIterator *glyphIterator,
109    const LEReferenceTo<ClassDefinitionTable> &classDefinitionTable,
110    LEErrorCode &success,
111    le_bool backtrack)
112{
113    if (LE_FAILURE(success)) { return FALSE; }
114
115    le_int32 direction = 1;
116    le_int32 match = 0;
117
118    if (backtrack) {
119        match = glyphCount - 1;
120        direction = -1;
121    }
122
123    while (glyphCount > 0) {
124        if (! glyphIterator->next()) {
125            return FALSE;
126        }
127
128        LEGlyphID glyph = glyphIterator->getCurrGlyphID();
129        le_int32 glyphClass = classDefinitionTable->getGlyphClass(classDefinitionTable, glyph, success);
130        le_int32 matchClass = SWAPW(classArray[match]);
131
132        if (glyphClass != matchClass) {
133            // Some fonts, e.g. Traditional Arabic, have classes
134            // in the class array which aren't in the class definition
135            // table. If we're looking for such a class, pretend that
136            // we found it.
137            if (classDefinitionTable->hasGlyphClass(classDefinitionTable, matchClass, success)) {
138                return FALSE;
139            }
140        }
141
142        glyphCount -= 1;
143        match += direction;
144    }
145
146    return TRUE;
147}
148
149le_bool ContextualSubstitutionBase::matchGlyphCoverages(const LEReferenceToArrayOf<Offset> &coverageTableOffsetArray, le_uint16 glyphCount,
150GlyphIterator *glyphIterator, const LETableReference &offsetBase, LEErrorCode &success, le_bool backtrack)
151{
152    le_int32 direction = 1;
153    le_int32 glyph = 0;
154
155    if (backtrack) {
156        glyph = glyphCount - 1;
157        direction = -1;
158    }
159
160    while (glyphCount > 0) {
161        Offset coverageTableOffset = SWAPW(coverageTableOffsetArray[glyph]);
162        LEReferenceTo<CoverageTable> coverageTable(offsetBase, success, coverageTableOffset);
163
164        if (LE_FAILURE(success) || ! glyphIterator->next()) {
165            return FALSE;
166        }
167
168        if (coverageTable->getGlyphCoverage(coverageTable,
169                                            (LEGlyphID) glyphIterator->getCurrGlyphID(),
170                                            success) < 0) {
171            return FALSE;
172        }
173
174        glyphCount -= 1;
175        glyph += direction;
176    }
177
178    return TRUE;
179}
180
181le_uint32 ContextualSubstitutionSubtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
182                                                  GlyphIterator *glyphIterator,
183                                                  const LEFontInstance *fontInstance,
184                                                  LEErrorCode& success) const
185{
186    if (LE_FAILURE(success)) {
187        return 0;
188    }
189
190    switch(SWAPW(subtableFormat))
191    {
192    case 0:
193        return 0;
194
195    case 1:
196    {
197      LEReferenceTo<ContextualSubstitutionFormat1Subtable> subtable(base, success, (const ContextualSubstitutionFormat1Subtable *) this);
198      if( LE_FAILURE(success) ) {
199        return 0;
200      }
201      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
202    }
203
204    case 2:
205    {
206      LEReferenceTo<ContextualSubstitutionFormat2Subtable> subtable(base, success, (const ContextualSubstitutionFormat2Subtable *) this);
207      if( LE_FAILURE(success) ) {
208        return 0;
209      }
210      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
211    }
212
213    case 3:
214    {
215      LEReferenceTo<ContextualSubstitutionFormat3Subtable> subtable(base, success, (const ContextualSubstitutionFormat3Subtable *) this);
216      if( LE_FAILURE(success) ) {
217        return 0;
218      }
219      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
220    }
221
222    default:
223        return 0;
224    }
225}
226
227le_uint32 ContextualSubstitutionFormat1Subtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
228                                                         GlyphIterator *glyphIterator,
229                                                         const LEFontInstance *fontInstance,
230                                                         LEErrorCode& success) const
231{
232    if (LE_FAILURE(success)) {
233        return 0;
234    }
235
236    LEGlyphID glyph = glyphIterator->getCurrGlyphID();
237    le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
238    if (LE_FAILURE(success)) {
239        return 0;
240    }
241
242    if (coverageIndex >= 0) {
243        le_uint16 srSetCount = SWAPW(subRuleSetCount);
244
245        if (coverageIndex < srSetCount) {
246            LEReferenceToArrayOf<Offset> subRuleSetTableOffsetArrayRef(base, success,
247                    &subRuleSetTableOffsetArray[coverageIndex], 1);
248            if (LE_FAILURE(success)) {
249                return 0;
250            }
251            Offset subRuleSetTableOffset = SWAPW(subRuleSetTableOffsetArray[coverageIndex]);
252            LEReferenceTo<SubRuleSetTable>
253                 subRuleSetTable(base, success, (const SubRuleSetTable *) ((char *) this + subRuleSetTableOffset));
254            le_uint16 subRuleCount = SWAPW(subRuleSetTable->subRuleCount);
255            le_int32 position = glyphIterator->getCurrStreamPosition();
256
257            LEReferenceToArrayOf<Offset> subRuleTableOffsetArrayRef(base, success,
258                    subRuleSetTable->subRuleTableOffsetArray, subRuleCount);
259            if (LE_FAILURE(success)) {
260                return 0;
261            }
262            for (le_uint16 subRule = 0; subRule < subRuleCount; subRule += 1) {
263                Offset subRuleTableOffset =
264                    SWAPW(subRuleSetTable->subRuleTableOffsetArray[subRule]);
265                LEReferenceTo<SubRuleTable>
266                     subRuleTable(subRuleSetTable, success, subRuleTableOffset);
267                le_uint16 matchCount = SWAPW(subRuleTable->glyphCount) - 1;
268                le_uint16 substCount = SWAPW(subRuleTable->substCount);
269                LEReferenceToArrayOf<TTGlyphID> inputGlyphArray(base, success, subRuleTable->inputGlyphArray, matchCount+2);
270                if (LE_FAILURE(success)) { return 0; }
271                if (matchGlyphIDs(inputGlyphArray, matchCount, glyphIterator)) {
272                  LEReferenceToArrayOf<SubstitutionLookupRecord>
273                    substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) &subRuleTable->inputGlyphArray[matchCount], substCount);
274
275                    applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
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
290le_uint32 ContextualSubstitutionFormat2Subtable::process(const LETableReference &base,
291         const LookupProcessor *lookupProcessor,
292         GlyphIterator *glyphIterator,
293         const LEFontInstance *fontInstance,
294         LEErrorCode& success) const
295{
296    if (LE_FAILURE(success)) {
297        return 0;
298    }
299
300    LEGlyphID glyph = glyphIterator->getCurrGlyphID();
301    le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
302    if (LE_FAILURE(success)) {
303        return 0;
304    }
305
306    if (coverageIndex >= 0) {
307        LEReferenceTo<ClassDefinitionTable> classDefinitionTable(base, success,
308                                                                 (const ClassDefinitionTable *) ((char *) this + SWAPW(classDefTableOffset)));
309        le_uint16 scSetCount = SWAPW(subClassSetCount);
310        le_int32 setClass = classDefinitionTable->getGlyphClass(classDefinitionTable,
311                                                                glyphIterator->getCurrGlyphID(),
312                                                                success);
313
314        if (setClass < scSetCount) {
315            LEReferenceToArrayOf<Offset>
316                 subClassSetTableOffsetArrayRef(base, success, subClassSetTableOffsetArray, setClass);
317            if (LE_FAILURE(success)) { return 0; }
318            if (subClassSetTableOffsetArray[setClass] != 0) {
319
320                Offset subClassSetTableOffset = SWAPW(subClassSetTableOffsetArray[setClass]);
321                LEReferenceTo<SubClassSetTable>
322                    subClassSetTable(base, success, (const SubClassSetTable *) ((char *) this + subClassSetTableOffset));
323                le_uint16 subClassRuleCount = SWAPW(subClassSetTable->subClassRuleCount);
324                le_int32 position = glyphIterator->getCurrStreamPosition();
325                LEReferenceToArrayOf<Offset>
326                    subClassRuleTableOffsetArrayRef(base, success, subClassSetTable->subClassRuleTableOffsetArray, subClassRuleCount);
327                if (LE_FAILURE(success)) {
328                    return 0;
329                }
330                for (le_uint16 scRule = 0; scRule < subClassRuleCount; scRule += 1) {
331                    Offset subClassRuleTableOffset =
332                        SWAPW(subClassSetTable->subClassRuleTableOffsetArray[scRule]);
333                    LEReferenceTo<SubClassRuleTable>
334                        subClassRuleTable(subClassSetTable, success, subClassRuleTableOffset);
335                    le_uint16 matchCount = SWAPW(subClassRuleTable->glyphCount) - 1;
336                    le_uint16 substCount = SWAPW(subClassRuleTable->substCount);
337
338                    LEReferenceToArrayOf<le_uint16> classArray(base, success, subClassRuleTable->classArray, matchCount+1);
339
340                    if (LE_FAILURE(success)) { return 0; }
341                    if (matchGlyphClasses(classArray, matchCount, glyphIterator, classDefinitionTable, success)) {
342                        LEReferenceToArrayOf<SubstitutionLookupRecord>
343                          substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) &subClassRuleTable->classArray[matchCount], substCount);
344
345                        applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
346
347                        return matchCount + 1;
348                    }
349
350                    glyphIterator->setCurrStreamPosition(position);
351                }
352            }
353        }
354
355        // XXX If we get here, the table is mal-formed...
356    }
357
358    return 0;
359}
360
361le_uint32 ContextualSubstitutionFormat3Subtable::process(const LETableReference &base,
362                                                         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    le_uint16 gCount = SWAPW(glyphCount);
372    le_uint16 subCount = SWAPW(substCount);
373    le_int32 position = glyphIterator->getCurrStreamPosition();
374
375    // Back up the glyph iterator so that we
376    // can call next() before the check, which
377    // will leave it pointing at the last glyph
378    // that matched when we're done.
379    glyphIterator->prev();
380
381    LEReferenceToArrayOf<Offset> covTableOffsetArray(base, success, coverageTableOffsetArray, gCount);
382
383    if( LE_FAILURE(success) ) { return 0; }
384
385    if (ContextualSubstitutionBase::matchGlyphCoverages(covTableOffsetArray, gCount, glyphIterator, base, success)) {
386        LEReferenceToArrayOf<SubstitutionLookupRecord>
387          substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) &coverageTableOffsetArray[gCount], subCount);
388
389        ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, subCount, glyphIterator, fontInstance, position, success);
390
391        return gCount + 1;
392    }
393
394    glyphIterator->setCurrStreamPosition(position);
395
396    return 0;
397}
398
399le_uint32 ChainingContextualSubstitutionSubtable::process(const LEReferenceTo<ChainingContextualSubstitutionSubtable> &base,
400                                                          const LookupProcessor *lookupProcessor,
401                                                          GlyphIterator *glyphIterator,
402                                                          const LEFontInstance *fontInstance,
403                                                          LEErrorCode& success) const
404{
405    if (LE_FAILURE(success)) {
406        return 0;
407    }
408
409    switch(SWAPW(subtableFormat))
410    {
411    case 0:
412        return 0;
413
414    case 1:
415    {
416      LEReferenceTo<ChainingContextualSubstitutionFormat1Subtable> subtable(base, success,  (ChainingContextualSubstitutionFormat1Subtable *) this);
417      if(LE_FAILURE(success)) return 0;
418      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
419    }
420
421    case 2:
422    {
423      LEReferenceTo<ChainingContextualSubstitutionFormat2Subtable> subtable(base, success, (const ChainingContextualSubstitutionFormat2Subtable *) this);
424      if( LE_FAILURE(success) ) { return 0; }
425      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
426    }
427
428    case 3:
429    {
430      LEReferenceTo<ChainingContextualSubstitutionFormat3Subtable> subtable(base, success, (const ChainingContextualSubstitutionFormat3Subtable *) this);
431      if( LE_FAILURE(success) ) { return 0; }
432      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
433    }
434
435    default:
436        return 0;
437    }
438}
439
440// NOTE: This could be a #define, but that seems to confuse
441// the Visual Studio .NET 2003 compiler on the calls to the
442// GlyphIterator constructor. It somehow can't decide if
443// emptyFeatureList matches an le_uint32 or an le_uint16...
444static const FeatureMask emptyFeatureList = 0x00000000UL;
445
446le_uint32 ChainingContextualSubstitutionFormat1Subtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
447                                                                 GlyphIterator *glyphIterator,
448                                                                 const LEFontInstance *fontInstance,
449                                                                 LEErrorCode& success) const
450{
451    if (LE_FAILURE(success)) {
452        return 0;
453    }
454
455    LEGlyphID glyph = glyphIterator->getCurrGlyphID();
456    le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
457    if (LE_FAILURE(success)) {
458        return 0;
459    }
460
461    if (coverageIndex >= 0) {
462        le_uint16 srSetCount = SWAPW(chainSubRuleSetCount);
463
464        if (coverageIndex < srSetCount) {
465            LEReferenceToArrayOf<Offset>
466                chainSubRuleSetTableOffsetArrayRef(base, success, chainSubRuleSetTableOffsetArray, coverageIndex);
467            if (LE_FAILURE(success)) {
468                return 0;
469            }
470            Offset chainSubRuleSetTableOffset = SWAPW(chainSubRuleSetTableOffsetArray[coverageIndex]);
471            LEReferenceTo<ChainSubRuleSetTable>
472                 chainSubRuleSetTable(base, success, (const ChainSubRuleSetTable *) ((char *) this + chainSubRuleSetTableOffset));
473            le_uint16 chainSubRuleCount = SWAPW(chainSubRuleSetTable->chainSubRuleCount);
474            le_int32 position = glyphIterator->getCurrStreamPosition();
475            GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
476            LEReferenceToArrayOf<Offset>
477                chainSubRuleTableOffsetArrayRef(base, success, chainSubRuleSetTable->chainSubRuleTableOffsetArray, chainSubRuleCount);
478            if (LE_FAILURE(success)) {
479                return 0;
480            }
481            for (le_uint16 subRule = 0; subRule < chainSubRuleCount; subRule += 1) {
482                Offset chainSubRuleTableOffset =
483                    SWAPW(chainSubRuleSetTable->chainSubRuleTableOffsetArray[subRule]);
484                LEReferenceTo<ChainSubRuleTable>
485                     chainSubRuleTable = LEReferenceTo<ChainSubRuleTable>(chainSubRuleSetTable, success, chainSubRuleTableOffset);
486                if( LE_FAILURE(success) ) { return 0; }
487                le_uint16 backtrackGlyphCount = SWAPW(chainSubRuleTable->backtrackGlyphCount);
488                LEReferenceToArrayOf<TTGlyphID> backtrackGlyphArray(base, success, chainSubRuleTable->backtrackGlyphArray, backtrackGlyphCount);
489                if( LE_FAILURE(success) ) { return 0; }
490                le_uint16 inputGlyphCount = (le_uint16) SWAPW(chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount]) - 1;
491                LEReferenceToArrayOf<TTGlyphID>   inputGlyphArray(base, success, &chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount + 1], inputGlyphCount+2);
492
493                if( LE_FAILURE(success) ) { return 0; }
494                le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputGlyphArray[inputGlyphCount]);
495                LEReferenceToArrayOf<TTGlyphID>   lookaheadGlyphArray(base, success, inputGlyphArray.getAlias(inputGlyphCount + 1,success), lookaheadGlyphCount+2);
496                if( LE_FAILURE(success) ) { return 0; }
497                le_uint16 substCount = (le_uint16) SWAPW(lookaheadGlyphArray[lookaheadGlyphCount]);
498
499                tempIterator.setCurrStreamPosition(position);
500
501                if (! tempIterator.prev(backtrackGlyphCount)) {
502                    continue;
503                }
504
505                tempIterator.prev();
506
507                if (! matchGlyphIDs(backtrackGlyphArray, backtrackGlyphCount, &tempIterator, TRUE)) {
508                    continue;
509                }
510
511                tempIterator.setCurrStreamPosition(position);
512                tempIterator.next(inputGlyphCount);
513                if (!matchGlyphIDs(lookaheadGlyphArray, lookaheadGlyphCount, &tempIterator)) {
514                    continue;
515                }
516
517                if (matchGlyphIDs(inputGlyphArray, inputGlyphCount, glyphIterator)) {
518                    LEReferenceToArrayOf<SubstitutionLookupRecord>
519                      substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) lookaheadGlyphArray.getAlias(lookaheadGlyphCount + 1,success), substCount);
520
521                    applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
522
523                    return inputGlyphCount + 1;
524                }
525
526                glyphIterator->setCurrStreamPosition(position);
527            }
528        }
529
530        // XXX If we get here, the table is mal-formed...
531    }
532
533    return 0;
534}
535
536le_uint32 ChainingContextualSubstitutionFormat2Subtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
537                                                                 GlyphIterator *glyphIterator,
538                                                                 const LEFontInstance *fontInstance,
539                                                                 LEErrorCode& success) const
540{
541    if (LE_FAILURE(success)) {
542        return 0;
543    }
544
545    LEGlyphID glyph = glyphIterator->getCurrGlyphID();
546    le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
547    if (LE_FAILURE(success)) {
548        return 0;
549    }
550
551    if (coverageIndex >= 0) {
552        LEReferenceTo<ClassDefinitionTable>
553             backtrackClassDefinitionTable(base, success, (const ClassDefinitionTable *) ((char *) this + SWAPW(backtrackClassDefTableOffset)));
554        LEReferenceTo<ClassDefinitionTable>
555             inputClassDefinitionTable(base, success, (const ClassDefinitionTable *) ((char *) this + SWAPW(inputClassDefTableOffset)));
556        LEReferenceTo<ClassDefinitionTable>
557             lookaheadClassDefinitionTable(base, success, (const ClassDefinitionTable *) ((char *) this + SWAPW(lookaheadClassDefTableOffset)));
558        le_uint16 scSetCount = SWAPW(chainSubClassSetCount);
559        le_int32 setClass = inputClassDefinitionTable->getGlyphClass(inputClassDefinitionTable,
560                                                                     glyphIterator->getCurrGlyphID(),
561                                                                     success);
562        LEReferenceToArrayOf<Offset>
563            chainSubClassSetTableOffsetArrayRef(base, success, chainSubClassSetTableOffsetArray, setClass);
564        if (LE_FAILURE(success)) {
565            return 0;
566        }
567
568        if (setClass < scSetCount && chainSubClassSetTableOffsetArray[setClass] != 0) {
569            Offset chainSubClassSetTableOffset = SWAPW(chainSubClassSetTableOffsetArray[setClass]);
570            LEReferenceTo<ChainSubClassSetTable>
571                 chainSubClassSetTable(base, success, (const ChainSubClassSetTable *) ((char *) this + chainSubClassSetTableOffset));
572            le_uint16 chainSubClassRuleCount = SWAPW(chainSubClassSetTable->chainSubClassRuleCount);
573            le_int32 position = glyphIterator->getCurrStreamPosition();
574            GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
575            LEReferenceToArrayOf<Offset>
576                chainSubClassRuleTableOffsetArrayRef(base, success, chainSubClassSetTable->chainSubClassRuleTableOffsetArray, chainSubClassRuleCount);
577            if (LE_FAILURE(success)) {
578                return 0;
579            }
580            for (le_uint16 scRule = 0; scRule < chainSubClassRuleCount; scRule += 1) {
581                Offset chainSubClassRuleTableOffset =
582                    SWAPW(chainSubClassSetTable->chainSubClassRuleTableOffsetArray[scRule]);
583                LEReferenceTo<ChainSubClassRuleTable>
584                     chainSubClassRuleTable(chainSubClassSetTable, success, chainSubClassRuleTableOffset);
585                le_uint16 backtrackGlyphCount = SWAPW(chainSubClassRuleTable->backtrackGlyphCount);
586                LEReferenceToArrayOf<le_uint16>   backtrackClassArray(base, success, chainSubClassRuleTable->backtrackClassArray, backtrackGlyphCount);
587                if( LE_FAILURE(success) ) { return 0; }
588                le_uint16 inputGlyphCount = SWAPW(chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount]) - 1;
589                LEReferenceToArrayOf<le_uint16>   inputClassArray(base, success, &chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount + 1],inputGlyphCount+2); // +2 for the lookaheadGlyphCount count
590                le_uint16 lookaheadGlyphCount = SWAPW(inputClassArray.getObject(inputGlyphCount, success));
591                LEReferenceToArrayOf<le_uint16>   lookaheadClassArray(base, success, inputClassArray.getAlias(inputGlyphCount + 1,success), lookaheadGlyphCount+2); // +2 for the substCount
592
593                if( LE_FAILURE(success) ) { return 0; }
594                le_uint16 substCount = SWAPW(lookaheadClassArray[lookaheadGlyphCount]);
595
596
597                tempIterator.setCurrStreamPosition(position);
598
599                if (! tempIterator.prev(backtrackGlyphCount)) {
600                    continue;
601                }
602
603                tempIterator.prev();
604                if (! matchGlyphClasses(backtrackClassArray, backtrackGlyphCount,
605                                        &tempIterator, backtrackClassDefinitionTable, success, TRUE)) {
606                    continue;
607                }
608
609                tempIterator.setCurrStreamPosition(position);
610                tempIterator.next(inputGlyphCount);
611                if (! matchGlyphClasses(lookaheadClassArray, lookaheadGlyphCount, &tempIterator, lookaheadClassDefinitionTable, success)) {
612                    continue;
613                }
614
615                if (matchGlyphClasses(inputClassArray, inputGlyphCount, glyphIterator, inputClassDefinitionTable, success)) {
616                    LEReferenceToArrayOf<SubstitutionLookupRecord>
617                      substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) lookaheadClassArray.getAlias(lookaheadGlyphCount + 1, success), substCount);
618                    if (LE_FAILURE(success)) { return 0; }
619                    applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
620
621                    return inputGlyphCount + 1;
622                }
623
624                glyphIterator->setCurrStreamPosition(position);
625            }
626        }
627
628        // XXX If we get here, the table is mal-formed...
629    }
630
631    return 0;
632}
633
634le_uint32 ChainingContextualSubstitutionFormat3Subtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
635                                                                 GlyphIterator *glyphIterator,
636                                                                 const LEFontInstance *fontInstance,
637                                                                 LEErrorCode & success) const
638{
639    if (LE_FAILURE(success)) {
640        return 0;
641    }
642
643    le_uint16 backtrkGlyphCount = SWAPW(backtrackGlyphCount);
644    LEReferenceToArrayOf<Offset> backtrackGlyphArrayRef(base, success, backtrackCoverageTableOffsetArray, backtrkGlyphCount);
645    if (LE_FAILURE(success)) {
646        return 0;
647    }
648    le_uint16 inputGlyphCount = (le_uint16) SWAPW(backtrackCoverageTableOffsetArray[backtrkGlyphCount]);
649    LEReferenceToArrayOf<Offset>   inputCoverageTableOffsetArray(base, success, &backtrackCoverageTableOffsetArray[backtrkGlyphCount + 1], inputGlyphCount+2); // offset
650    if (LE_FAILURE(success)) { return 0; }
651    const le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputCoverageTableOffsetArray[inputGlyphCount]);
652    LEReferenceToArrayOf<Offset>   lookaheadCoverageTableOffsetArray(base, success, inputCoverageTableOffsetArray.getAlias(inputGlyphCount + 1, success), lookaheadGlyphCount+2);
653
654    if( LE_FAILURE(success) ) { return 0; }
655    le_uint16 substCount = (le_uint16) SWAPW(lookaheadCoverageTableOffsetArray[lookaheadGlyphCount]);
656    le_int32 position = glyphIterator->getCurrStreamPosition();
657    GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
658
659    if (! tempIterator.prev(backtrkGlyphCount)) {
660        return 0;
661    }
662
663    tempIterator.prev();
664    if (! ContextualSubstitutionBase::matchGlyphCoverages(backtrackCoverageTableOffsetArray,
665                       backtrkGlyphCount, &tempIterator, base, success, TRUE)) {
666        return 0;
667    }
668
669    tempIterator.setCurrStreamPosition(position);
670    tempIterator.next(inputGlyphCount - 1);
671    if (! ContextualSubstitutionBase::matchGlyphCoverages(lookaheadCoverageTableOffsetArray,
672                        lookaheadGlyphCount, &tempIterator, base, success)) {
673        return 0;
674    }
675
676    // Back up the glyph iterator so that we
677    // can call next() before the check, which
678    // will leave it pointing at the last glyph
679    // that matched when we're done.
680    glyphIterator->prev();
681
682    if (ContextualSubstitutionBase::matchGlyphCoverages(inputCoverageTableOffsetArray,
683                                                        inputGlyphCount, glyphIterator, base, success)) {
684        LEReferenceToArrayOf<SubstitutionLookupRecord>
685          substLookupRecordArray(base, success,
686                                 (const SubstitutionLookupRecord *) lookaheadCoverageTableOffsetArray.getAlias(lookaheadGlyphCount + 1,success), substCount);
687
688        ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
689
690        return inputGlyphCount;
691    }
692
693    glyphIterator->setCurrStreamPosition(position);
694
695    return 0;
696}
697
698U_NAMESPACE_END
699