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