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>
247                subRuleSetTableOffsetArrayRef(base, success, subRuleSetTableOffsetArray, srSetCount);
248            if (LE_FAILURE(success)) {
249                return 0;
250            }
251            Offset subRuleSetTableOffset = SWAPW(subRuleSetTableOffsetArray[coverageIndex]);
252            LEReferenceTo<SubRuleSetTable> subRuleSetTable(base, success, subRuleSetTableOffset);
253            if (LE_FAILURE(success)) { return 0; }
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                if (LE_FAILURE(success)) { return 0; }
268                le_uint16 matchCount = SWAPW(subRuleTable->glyphCount) - 1;
269                le_uint16 substCount = SWAPW(subRuleTable->substCount);
270                LEReferenceToArrayOf<TTGlyphID> inputGlyphArray(base, success, subRuleTable->inputGlyphArray, matchCount+2);
271                if (LE_FAILURE(success)) { return 0; }
272                if (matchGlyphIDs(inputGlyphArray, matchCount, glyphIterator)) {
273                  LEReferenceToArrayOf<SubstitutionLookupRecord>
274                    substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) &subRuleTable->inputGlyphArray[matchCount], substCount);
275
276                    applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
277
278                    return matchCount + 1;
279                }
280
281                glyphIterator->setCurrStreamPosition(position);
282            }
283        }
284
285        // XXX If we get here, the table is mal-formed...
286    }
287
288    return 0;
289}
290
291le_uint32 ContextualSubstitutionFormat2Subtable::process(const LETableReference &base,
292         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    LEGlyphID glyph = glyphIterator->getCurrGlyphID();
302    le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
303    if (LE_FAILURE(success)) {
304        return 0;
305    }
306
307    if (coverageIndex >= 0) {
308        LEReferenceTo<ClassDefinitionTable> classDefinitionTable(base, success, SWAPW(classDefTableOffset));
309        if (LE_FAILURE(success)) { return 0; }
310        le_uint16 scSetCount = SWAPW(subClassSetCount);
311        le_int32 setClass = classDefinitionTable->getGlyphClass(classDefinitionTable,
312                                                                glyphIterator->getCurrGlyphID(),
313                                                                success);
314
315        if (setClass < scSetCount) {
316            LEReferenceToArrayOf<Offset>
317                subClassSetTableOffsetArrayRef(base, success, subClassSetTableOffsetArray, scSetCount);
318            if (LE_FAILURE(success)) { return 0; }
319            if (subClassSetTableOffsetArray[setClass] != 0) {
320
321                Offset subClassSetTableOffset = SWAPW(subClassSetTableOffsetArray[setClass]);
322                LEReferenceTo<SubClassSetTable> subClassSetTable(base, success, subClassSetTableOffset);
323                if (LE_FAILURE(success)) { return 0; }
324                le_uint16 subClassRuleCount = SWAPW(subClassSetTable->subClassRuleCount);
325                le_int32 position = glyphIterator->getCurrStreamPosition();
326                LEReferenceToArrayOf<Offset>
327                    subClassRuleTableOffsetArrayRef(base, success, subClassSetTable->subClassRuleTableOffsetArray, subClassRuleCount);
328                if (LE_FAILURE(success)) {
329                    return 0;
330                }
331                for (le_uint16 scRule = 0; scRule < subClassRuleCount; scRule += 1) {
332                    Offset subClassRuleTableOffset =
333                        SWAPW(subClassSetTable->subClassRuleTableOffsetArray[scRule]);
334                    LEReferenceTo<SubClassRuleTable>
335                        subClassRuleTable(subClassSetTable, success, subClassRuleTableOffset);
336                    if (LE_FAILURE(success)) { return 0; }
337                    le_uint16 matchCount = SWAPW(subClassRuleTable->glyphCount) - 1;
338                    le_uint16 substCount = SWAPW(subClassRuleTable->substCount);
339
340                    LEReferenceToArrayOf<le_uint16> classArray(base, success, subClassRuleTable->classArray, matchCount+1);
341
342                    if (LE_FAILURE(success)) { return 0; }
343                    if (matchGlyphClasses(classArray, matchCount, glyphIterator, classDefinitionTable, success)) {
344                        LEReferenceToArrayOf<SubstitutionLookupRecord>
345                          substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) &subClassRuleTable->classArray[matchCount], substCount);
346
347                        applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
348
349                        return matchCount + 1;
350                    }
351
352                    glyphIterator->setCurrStreamPosition(position);
353                }
354            }
355        }
356
357        // XXX If we get here, the table is mal-formed...
358    }
359
360    return 0;
361}
362
363le_uint32 ContextualSubstitutionFormat3Subtable::process(const LETableReference &base,
364                                                         const LookupProcessor *lookupProcessor,
365                                                         GlyphIterator *glyphIterator,
366                                                         const LEFontInstance *fontInstance,
367                                                         LEErrorCode& success)const
368{
369    if (LE_FAILURE(success)) {
370        return 0;
371    }
372
373    le_uint16 gCount = SWAPW(glyphCount);
374    le_uint16 subCount = SWAPW(substCount);
375    le_int32 position = glyphIterator->getCurrStreamPosition();
376
377    // Back up the glyph iterator so that we
378    // can call next() before the check, which
379    // will leave it pointing at the last glyph
380    // that matched when we're done.
381    glyphIterator->prev();
382
383    LEReferenceToArrayOf<Offset> covTableOffsetArray(base, success, coverageTableOffsetArray, gCount);
384
385    if( LE_FAILURE(success) ) { return 0; }
386
387    if (ContextualSubstitutionBase::matchGlyphCoverages(covTableOffsetArray, gCount, glyphIterator, base, success)) {
388        LEReferenceToArrayOf<SubstitutionLookupRecord>
389          substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) &coverageTableOffsetArray[gCount], subCount);
390
391        ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, subCount, glyphIterator, fontInstance, position, success);
392
393        return gCount + 1;
394    }
395
396    glyphIterator->setCurrStreamPosition(position);
397
398    return 0;
399}
400
401le_uint32 ChainingContextualSubstitutionSubtable::process(const LEReferenceTo<ChainingContextualSubstitutionSubtable> &base,
402                                                          const LookupProcessor *lookupProcessor,
403                                                          GlyphIterator *glyphIterator,
404                                                          const LEFontInstance *fontInstance,
405                                                          LEErrorCode& success) const
406{
407    if (LE_FAILURE(success)) {
408        return 0;
409    }
410
411    switch(SWAPW(subtableFormat))
412    {
413    case 0:
414        return 0;
415
416    case 1:
417    {
418      LEReferenceTo<ChainingContextualSubstitutionFormat1Subtable> subtable(base, success,  (ChainingContextualSubstitutionFormat1Subtable *) this);
419      if(LE_FAILURE(success)) return 0;
420      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
421    }
422
423    case 2:
424    {
425      LEReferenceTo<ChainingContextualSubstitutionFormat2Subtable> subtable(base, success, (const ChainingContextualSubstitutionFormat2Subtable *) this);
426      if( LE_FAILURE(success) ) { return 0; }
427      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
428    }
429
430    case 3:
431    {
432      LEReferenceTo<ChainingContextualSubstitutionFormat3Subtable> subtable(base, success, (const ChainingContextualSubstitutionFormat3Subtable *) this);
433      if( LE_FAILURE(success) ) { return 0; }
434      return subtable->process(subtable, lookupProcessor, glyphIterator, fontInstance, success);
435    }
436
437    default:
438        return 0;
439    }
440}
441
442// NOTE: This could be a #define, but that seems to confuse
443// the Visual Studio .NET 2003 compiler on the calls to the
444// GlyphIterator constructor. It somehow can't decide if
445// emptyFeatureList matches an le_uint32 or an le_uint16...
446static const FeatureMask emptyFeatureList = 0x00000000UL;
447
448le_uint32 ChainingContextualSubstitutionFormat1Subtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
449                                                                 GlyphIterator *glyphIterator,
450                                                                 const LEFontInstance *fontInstance,
451                                                                 LEErrorCode& success) const
452{
453    if (LE_FAILURE(success)) {
454        return 0;
455    }
456
457    LEGlyphID glyph = glyphIterator->getCurrGlyphID();
458    le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
459    if (LE_FAILURE(success)) {
460        return 0;
461    }
462
463    if (coverageIndex >= 0) {
464        le_uint16 srSetCount = SWAPW(chainSubRuleSetCount);
465
466        if (coverageIndex < srSetCount) {
467            LEReferenceToArrayOf<Offset>
468                chainSubRuleSetTableOffsetArrayRef(base, success, chainSubRuleSetTableOffsetArray, srSetCount);
469            if (LE_FAILURE(success)) {
470                return 0;
471            }
472            Offset chainSubRuleSetTableOffset = SWAPW(chainSubRuleSetTableOffsetArray[coverageIndex]);
473            LEReferenceTo<ChainSubRuleSetTable> chainSubRuleSetTable(base, success, chainSubRuleSetTableOffset);
474            if (LE_FAILURE(success)) { return 0; }
475            le_uint16 chainSubRuleCount = SWAPW(chainSubRuleSetTable->chainSubRuleCount);
476            le_int32 position = glyphIterator->getCurrStreamPosition();
477            GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
478            LEReferenceToArrayOf<Offset>
479                chainSubRuleTableOffsetArrayRef(base, success, chainSubRuleSetTable->chainSubRuleTableOffsetArray, chainSubRuleCount);
480            if (LE_FAILURE(success)) {
481                return 0;
482            }
483            for (le_uint16 subRule = 0; subRule < chainSubRuleCount; subRule += 1) {
484                Offset chainSubRuleTableOffset =
485                    SWAPW(chainSubRuleSetTable->chainSubRuleTableOffsetArray[subRule]);
486                LEReferenceTo<ChainSubRuleTable>
487                     chainSubRuleTable = LEReferenceTo<ChainSubRuleTable>(chainSubRuleSetTable, success, chainSubRuleTableOffset);
488                if( LE_FAILURE(success) ) { return 0; }
489                le_uint16 backtrackGlyphCount = SWAPW(chainSubRuleTable->backtrackGlyphCount);
490                LEReferenceToArrayOf<TTGlyphID> backtrackGlyphArray(base, success, chainSubRuleTable->backtrackGlyphArray, backtrackGlyphCount);
491                if( LE_FAILURE(success) ) { return 0; }
492                le_uint16 inputGlyphCount = (le_uint16) SWAPW(chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount]) - 1;
493                LEReferenceToArrayOf<TTGlyphID>   inputGlyphArray(base, success, &chainSubRuleTable->backtrackGlyphArray[backtrackGlyphCount + 1], inputGlyphCount+2);
494
495                if( LE_FAILURE(success) ) { return 0; }
496                le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputGlyphArray[inputGlyphCount]);
497                LEReferenceToArrayOf<TTGlyphID>   lookaheadGlyphArray(base, success, inputGlyphArray.getAlias(inputGlyphCount + 1,success), lookaheadGlyphCount+2);
498                if( LE_FAILURE(success) ) { return 0; }
499                le_uint16 substCount = (le_uint16) SWAPW(lookaheadGlyphArray[lookaheadGlyphCount]);
500
501                tempIterator.setCurrStreamPosition(position);
502
503                if (! tempIterator.prev(backtrackGlyphCount)) {
504                    continue;
505                }
506
507                tempIterator.prev();
508
509                if (! matchGlyphIDs(backtrackGlyphArray, backtrackGlyphCount, &tempIterator, TRUE)) {
510                    continue;
511                }
512
513                tempIterator.setCurrStreamPosition(position);
514                tempIterator.next(inputGlyphCount);
515                if (!matchGlyphIDs(lookaheadGlyphArray, lookaheadGlyphCount, &tempIterator)) {
516                    continue;
517                }
518
519                if (matchGlyphIDs(inputGlyphArray, inputGlyphCount, glyphIterator)) {
520                    LEReferenceToArrayOf<SubstitutionLookupRecord>
521                      substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) lookaheadGlyphArray.getAlias(lookaheadGlyphCount + 1,success), substCount);
522
523                    applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
524
525                    return inputGlyphCount + 1;
526                }
527
528                glyphIterator->setCurrStreamPosition(position);
529            }
530        }
531
532        // XXX If we get here, the table is mal-formed...
533    }
534
535    return 0;
536}
537
538le_uint32 ChainingContextualSubstitutionFormat2Subtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
539                                                                 GlyphIterator *glyphIterator,
540                                                                 const LEFontInstance *fontInstance,
541                                                                 LEErrorCode& success) const
542{
543    if (LE_FAILURE(success)) {
544        return 0;
545    }
546
547    LEGlyphID glyph = glyphIterator->getCurrGlyphID();
548    le_int32 coverageIndex = getGlyphCoverage(lookupProcessor->getReference(), glyph, success);
549    if (LE_FAILURE(success)) {
550        return 0;
551    }
552
553    if (coverageIndex >= 0) {
554        LEReferenceTo<ClassDefinitionTable>
555             backtrackClassDefinitionTable(base, success, SWAPW(backtrackClassDefTableOffset));
556        LEReferenceTo<ClassDefinitionTable>
557             inputClassDefinitionTable(base, success, SWAPW(inputClassDefTableOffset));
558        LEReferenceTo<ClassDefinitionTable>
559             lookaheadClassDefinitionTable(base, success, SWAPW(lookaheadClassDefTableOffset));
560        le_uint16 scSetCount = SWAPW(chainSubClassSetCount);
561        le_int32 setClass = inputClassDefinitionTable->getGlyphClass(inputClassDefinitionTable,
562                                                                     glyphIterator->getCurrGlyphID(),
563                                                                     success);
564        LEReferenceToArrayOf<Offset>
565            chainSubClassSetTableOffsetArrayRef(base, success, chainSubClassSetTableOffsetArray, scSetCount);
566        if (LE_FAILURE(success)) {
567            return 0;
568        }
569
570        if (setClass < scSetCount && chainSubClassSetTableOffsetArray[setClass] != 0) {
571            Offset chainSubClassSetTableOffset = SWAPW(chainSubClassSetTableOffsetArray[setClass]);
572            LEReferenceTo<ChainSubClassSetTable>
573                 chainSubClassSetTable(base, success, chainSubClassSetTableOffset);
574            if (LE_FAILURE(success)) { return 0; }
575            le_uint16 chainSubClassRuleCount = SWAPW(chainSubClassSetTable->chainSubClassRuleCount);
576            le_int32 position = glyphIterator->getCurrStreamPosition();
577            GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
578            LEReferenceToArrayOf<Offset>
579                chainSubClassRuleTableOffsetArrayRef(base, success, chainSubClassSetTable->chainSubClassRuleTableOffsetArray, chainSubClassRuleCount);
580            if (LE_FAILURE(success)) {
581                return 0;
582            }
583            for (le_uint16 scRule = 0; scRule < chainSubClassRuleCount; scRule += 1) {
584                Offset chainSubClassRuleTableOffset =
585                    SWAPW(chainSubClassSetTable->chainSubClassRuleTableOffsetArray[scRule]);
586                LEReferenceTo<ChainSubClassRuleTable>
587                     chainSubClassRuleTable(chainSubClassSetTable, success, chainSubClassRuleTableOffset);
588                if (LE_FAILURE(success)) { return 0; }
589                le_uint16 backtrackGlyphCount = SWAPW(chainSubClassRuleTable->backtrackGlyphCount);
590                LEReferenceToArrayOf<le_uint16>   backtrackClassArray(base, success, chainSubClassRuleTable->backtrackClassArray, backtrackGlyphCount);
591                if( LE_FAILURE(success) ) { return 0; }
592                le_uint16 inputGlyphCount = SWAPW(chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount]) - 1;
593                LEReferenceToArrayOf<le_uint16>   inputClassArray(base, success, &chainSubClassRuleTable->backtrackClassArray[backtrackGlyphCount + 1],inputGlyphCount+2); // +2 for the lookaheadGlyphCount count
594                le_uint16 lookaheadGlyphCount = SWAPW(inputClassArray.getObject(inputGlyphCount, success));
595                LEReferenceToArrayOf<le_uint16>   lookaheadClassArray(base, success, inputClassArray.getAlias(inputGlyphCount + 1,success), lookaheadGlyphCount+2); // +2 for the substCount
596
597                if( LE_FAILURE(success) ) { return 0; }
598                le_uint16 substCount = SWAPW(lookaheadClassArray[lookaheadGlyphCount]);
599
600
601                tempIterator.setCurrStreamPosition(position);
602
603                if (! tempIterator.prev(backtrackGlyphCount)) {
604                    continue;
605                }
606
607                tempIterator.prev();
608                if (! matchGlyphClasses(backtrackClassArray, backtrackGlyphCount,
609                                        &tempIterator, backtrackClassDefinitionTable, success, TRUE)) {
610                    continue;
611                }
612
613                tempIterator.setCurrStreamPosition(position);
614                tempIterator.next(inputGlyphCount);
615                if (! matchGlyphClasses(lookaheadClassArray, lookaheadGlyphCount, &tempIterator, lookaheadClassDefinitionTable, success)) {
616                    continue;
617                }
618
619                if (matchGlyphClasses(inputClassArray, inputGlyphCount, glyphIterator, inputClassDefinitionTable, success)) {
620                    LEReferenceToArrayOf<SubstitutionLookupRecord>
621                      substLookupRecordArray(base, success, (const SubstitutionLookupRecord *) lookaheadClassArray.getAlias(lookaheadGlyphCount + 1, success), substCount);
622                    if (LE_FAILURE(success)) { return 0; }
623                    applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
624
625                    return inputGlyphCount + 1;
626                }
627
628                glyphIterator->setCurrStreamPosition(position);
629            }
630        }
631
632        // XXX If we get here, the table is mal-formed...
633    }
634
635    return 0;
636}
637
638le_uint32 ChainingContextualSubstitutionFormat3Subtable::process(const LETableReference &base, const LookupProcessor *lookupProcessor,
639                                                                 GlyphIterator *glyphIterator,
640                                                                 const LEFontInstance *fontInstance,
641                                                                 LEErrorCode & success) const
642{
643    if (LE_FAILURE(success)) {
644        return 0;
645    }
646
647    le_uint16 backtrkGlyphCount = SWAPW(backtrackGlyphCount);
648    LEReferenceToArrayOf<Offset> backtrackGlyphArrayRef(base, success, backtrackCoverageTableOffsetArray, backtrkGlyphCount);
649    if (LE_FAILURE(success)) {
650        return 0;
651    }
652    le_uint16 inputGlyphCount = (le_uint16) SWAPW(backtrackCoverageTableOffsetArray[backtrkGlyphCount]);
653    LEReferenceToArrayOf<Offset>   inputCoverageTableOffsetArray(base, success, &backtrackCoverageTableOffsetArray[backtrkGlyphCount + 1], inputGlyphCount+2); // offset
654    if (LE_FAILURE(success)) { return 0; }
655    const le_uint16 lookaheadGlyphCount = (le_uint16) SWAPW(inputCoverageTableOffsetArray[inputGlyphCount]);
656    LEReferenceToArrayOf<Offset>   lookaheadCoverageTableOffsetArray(base, success, inputCoverageTableOffsetArray.getAlias(inputGlyphCount + 1, success), lookaheadGlyphCount+2);
657
658    if( LE_FAILURE(success) ) { return 0; }
659    le_uint16 substCount = (le_uint16) SWAPW(lookaheadCoverageTableOffsetArray[lookaheadGlyphCount]);
660    le_int32 position = glyphIterator->getCurrStreamPosition();
661    GlyphIterator tempIterator(*glyphIterator, emptyFeatureList);
662
663    if (! tempIterator.prev(backtrkGlyphCount)) {
664        return 0;
665    }
666
667    tempIterator.prev();
668    if (! ContextualSubstitutionBase::matchGlyphCoverages(backtrackCoverageTableOffsetArray,
669                       backtrkGlyphCount, &tempIterator, base, success, TRUE)) {
670        return 0;
671    }
672
673    tempIterator.setCurrStreamPosition(position);
674    tempIterator.next(inputGlyphCount - 1);
675    if (! ContextualSubstitutionBase::matchGlyphCoverages(lookaheadCoverageTableOffsetArray,
676                        lookaheadGlyphCount, &tempIterator, base, success)) {
677        return 0;
678    }
679
680    // Back up the glyph iterator so that we
681    // can call next() before the check, which
682    // will leave it pointing at the last glyph
683    // that matched when we're done.
684    glyphIterator->prev();
685
686    if (ContextualSubstitutionBase::matchGlyphCoverages(inputCoverageTableOffsetArray,
687                                                        inputGlyphCount, glyphIterator, base, success)) {
688        LEReferenceToArrayOf<SubstitutionLookupRecord>
689          substLookupRecordArray(base, success,
690                                 (const SubstitutionLookupRecord *) lookaheadCoverageTableOffsetArray.getAlias(lookaheadGlyphCount + 1,success), substCount);
691
692        ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor, substLookupRecordArray, substCount, glyphIterator, fontInstance, position, success);
693
694        return inputGlyphCount;
695    }
696
697    glyphIterator->setCurrStreamPosition(position);
698
699    return 0;
700}
701
702U_NAMESPACE_END
703