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