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