3 * (C) Copyright IBM Corp. 1998-2004 - All Rights Reserved
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"
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.
25 void ContextualSubstitutionBase::applySubstitutionLookups(
26 const LookupProcessor
*lookupProcessor
,
27 const SubstitutionLookupRecord
*substLookupRecordArray
,
29 GlyphIterator
*glyphIterator
,
30 const LEFontInstance
*fontInstance
,
33 GlyphIterator
tempIterator(*glyphIterator
);
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
);
39 tempIterator
.setCurrStreamPosition(position
);
40 tempIterator
.next(sequenceIndex
);
42 lookupProcessor
->applySingleLookup(lookupListIndex
, &tempIterator
, fontInstance
);
46 le_bool
ContextualSubstitutionBase::matchGlyphIDs(const TTGlyphID
*glyphArray
, le_uint16 glyphCount
,
47 GlyphIterator
*glyphIterator
, le_bool backtrack
)
49 le_int32 direction
= 1;
53 match
= glyphCount
-1;
57 while (glyphCount
> 0) {
58 if (! glyphIterator
->next()) {
62 TTGlyphID glyph
= (TTGlyphID
) glyphIterator
->getCurrGlyphID();
64 if (glyph
!= SWAPW(glyphArray
[match
])) {
75 le_bool
ContextualSubstitutionBase::matchGlyphClasses(const le_uint16
*classArray
, le_uint16 glyphCount
,
76 GlyphIterator
*glyphIterator
,
77 const ClassDefinitionTable
*classDefinitionTable
,
80 le_int32 direction
= 1;
84 match
= glyphCount
- 1;
88 while (glyphCount
> 0) {
89 if (! glyphIterator
->next()) {
93 LEGlyphID glyph
= glyphIterator
->getCurrGlyphID();
94 le_int32 glyphClass
= classDefinitionTable
->getGlyphClass(glyph
);
95 le_int32 matchClass
= SWAPW(classArray
[match
]);
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
102 if (classDefinitionTable
->hasGlyphClass(matchClass
)) {
114 le_bool
ContextualSubstitutionBase::matchGlyphCoverages(const Offset
*coverageTableOffsetArray
, le_uint16 glyphCount
,
115 GlyphIterator
*glyphIterator
, const char *offsetBase
, le_bool backtrack
)
117 le_int32 direction
= 1;
121 glyph
= glyphCount
- 1;
125 while (glyphCount
> 0) {
126 Offset coverageTableOffset
= SWAPW(coverageTableOffsetArray
[glyph
]);
127 const CoverageTable
*coverageTable
= (const CoverageTable
*) (offsetBase
+ coverageTableOffset
);
129 if (! glyphIterator
->next()) {
133 if (coverageTable
->getGlyphCoverage((LEGlyphID
) glyphIterator
->getCurrGlyphID()) < 0) {
144 le_uint32
ContextualSubstitutionSubtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
145 const LEFontInstance
*fontInstance
) const
147 switch(SWAPW(subtableFormat
))
154 const ContextualSubstitutionFormat1Subtable
*subtable
= (const ContextualSubstitutionFormat1Subtable
*) this;
156 return subtable
->process(lookupProcessor
, glyphIterator
, fontInstance
);
161 const ContextualSubstitutionFormat2Subtable
*subtable
= (const ContextualSubstitutionFormat2Subtable
*) this;
163 return subtable
->process(lookupProcessor
, glyphIterator
, fontInstance
);
168 const ContextualSubstitutionFormat3Subtable
*subtable
= (const ContextualSubstitutionFormat3Subtable
*) this;
170 return subtable
->process(lookupProcessor
, glyphIterator
, fontInstance
);
178 le_uint32
ContextualSubstitutionFormat1Subtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
179 const LEFontInstance
*fontInstance
) const
181 LEGlyphID glyph
= glyphIterator
->getCurrGlyphID();
182 le_int32 coverageIndex
= getGlyphCoverage(glyph
);
184 if (coverageIndex
>= 0) {
185 le_uint16 srSetCount
= SWAPW(subRuleSetCount
);
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();
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
);
202 if (matchGlyphIDs(subRuleTable
->inputGlyphArray
, matchCount
, glyphIterator
)) {
203 const SubstitutionLookupRecord
*substLookupRecordArray
=
204 (const SubstitutionLookupRecord
*) &subRuleTable
->inputGlyphArray
[matchCount
];
206 applySubstitutionLookups(lookupProcessor
, substLookupRecordArray
, substCount
, glyphIterator
, fontInstance
, position
);
208 return matchCount
+ 1;
211 glyphIterator
->setCurrStreamPosition(position
);
215 // XXX If we get here, the table is mal-formed...
221 le_uint32
ContextualSubstitutionFormat2Subtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
222 const LEFontInstance
*fontInstance
) const
224 LEGlyphID glyph
= glyphIterator
->getCurrGlyphID();
225 le_int32 coverageIndex
= getGlyphCoverage(glyph
);
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());
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();
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
);
248 if (matchGlyphClasses(subClassRuleTable
->classArray
, matchCount
, glyphIterator
, classDefinitionTable
)) {
249 const SubstitutionLookupRecord
*substLookupRecordArray
=
250 (const SubstitutionLookupRecord
*) &subClassRuleTable
->classArray
[matchCount
];
252 applySubstitutionLookups(lookupProcessor
, substLookupRecordArray
, substCount
, glyphIterator
, fontInstance
, position
);
254 return matchCount
+ 1;
257 glyphIterator
->setCurrStreamPosition(position
);
261 // XXX If we get here, the table is mal-formed...
267 le_uint32
ContextualSubstitutionFormat3Subtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
268 const LEFontInstance
*fontInstance
)const
270 le_uint16 gCount
= SWAPW(glyphCount
);
271 le_uint16 subCount
= SWAPW(substCount
);
272 le_int32 position
= glyphIterator
->getCurrStreamPosition();
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();
280 if (ContextualSubstitutionBase::matchGlyphCoverages(coverageTableOffsetArray
, gCount
, glyphIterator
, (const char *) this)) {
281 const SubstitutionLookupRecord
*substLookupRecordArray
=
282 (const SubstitutionLookupRecord
*) &coverageTableOffsetArray
[gCount
];
284 ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor
, substLookupRecordArray
, subCount
, glyphIterator
, fontInstance
, position
);
289 glyphIterator
->setCurrStreamPosition(position
);
294 le_uint32
ChainingContextualSubstitutionSubtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
295 const LEFontInstance
*fontInstance
) const
297 switch(SWAPW(subtableFormat
))
304 const ChainingContextualSubstitutionFormat1Subtable
*subtable
= (const ChainingContextualSubstitutionFormat1Subtable
*) this;
306 return subtable
->process(lookupProcessor
, glyphIterator
, fontInstance
);
311 const ChainingContextualSubstitutionFormat2Subtable
*subtable
= (const ChainingContextualSubstitutionFormat2Subtable
*) this;
313 return subtable
->process(lookupProcessor
, glyphIterator
, fontInstance
);
318 const ChainingContextualSubstitutionFormat3Subtable
*subtable
= (const ChainingContextualSubstitutionFormat3Subtable
*) this;
320 return subtable
->process(lookupProcessor
, glyphIterator
, fontInstance
);
328 static const LETag emptyTag
= 0;
330 le_uint32
ChainingContextualSubstitutionFormat1Subtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
331 const LEFontInstance
*fontInstance
) const
333 LEGlyphID glyph
= glyphIterator
->getCurrGlyphID();
334 le_int32 coverageIndex
= getGlyphCoverage(glyph
);
336 if (coverageIndex
>= 0) {
337 le_uint16 srSetCount
= SWAPW(chainSubRuleSetCount
);
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
);
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
]);
359 tempIterator
.setCurrStreamPosition(position
);
361 if (! tempIterator
.prev(backtrackGlyphCount
)) {
366 if (! matchGlyphIDs(chainSubRuleTable
->backtrackGlyphArray
, backtrackGlyphCount
, &tempIterator
, TRUE
)) {
370 tempIterator
.setCurrStreamPosition(position
);
371 tempIterator
.next(inputGlyphCount
);
372 if (!matchGlyphIDs(lookaheadGlyphArray
, lookaheadGlyphCount
, &tempIterator
)) {
376 if (matchGlyphIDs(inputGlyphArray
, inputGlyphCount
, glyphIterator
)) {
377 const SubstitutionLookupRecord
*substLookupRecordArray
=
378 (const SubstitutionLookupRecord
*) &lookaheadGlyphArray
[lookaheadGlyphCount
+ 1];
380 applySubstitutionLookups(lookupProcessor
, substLookupRecordArray
, substCount
, glyphIterator
, fontInstance
, position
);
382 return inputGlyphCount
+ 1;
385 glyphIterator
->setCurrStreamPosition(position
);
389 // XXX If we get here, the table is mal-formed...
395 le_uint32
ChainingContextualSubstitutionFormat2Subtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
396 const LEFontInstance
*fontInstance
) const
398 LEGlyphID glyph
= glyphIterator
->getCurrGlyphID();
399 le_int32 coverageIndex
= getGlyphCoverage(glyph
);
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());
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
);
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
]);
432 tempIterator
.setCurrStreamPosition(position
);
434 if (! tempIterator
.prev(backtrackGlyphCount
)) {
439 if (! matchGlyphClasses(chainSubClassRuleTable
->backtrackClassArray
, backtrackGlyphCount
,
440 &tempIterator
, backtrackClassDefinitionTable
, TRUE
)) {
444 tempIterator
.setCurrStreamPosition(position
);
445 tempIterator
.next(inputGlyphCount
);
446 if (! matchGlyphClasses(lookaheadClassArray
, lookaheadGlyphCount
, &tempIterator
, lookaheadClassDefinitionTable
)) {
450 if (matchGlyphClasses(inputClassArray
, inputGlyphCount
, glyphIterator
, inputClassDefinitionTable
)) {
451 const SubstitutionLookupRecord
*substLookupRecordArray
=
452 (const SubstitutionLookupRecord
*) &lookaheadClassArray
[lookaheadGlyphCount
+ 1];
454 applySubstitutionLookups(lookupProcessor
, substLookupRecordArray
, substCount
, glyphIterator
, fontInstance
, position
);
456 return inputGlyphCount
+ 1;
459 glyphIterator
->setCurrStreamPosition(position
);
463 // XXX If we get here, the table is mal-formed...
469 le_uint32
ChainingContextualSubstitutionFormat3Subtable::process(const LookupProcessor
*lookupProcessor
, GlyphIterator
*glyphIterator
,
470 const LEFontInstance
*fontInstance
) const
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
);
481 if (! tempIterator
.prev(backtrkGlyphCount
)) {
486 if (! ContextualSubstitutionBase::matchGlyphCoverages(backtrackCoverageTableOffsetArray
,
487 backtrkGlyphCount
, &tempIterator
, (const char *) this, TRUE
)) {
491 tempIterator
.setCurrStreamPosition(position
);
492 tempIterator
.next(inputGlyphCount
- 1);
493 if (! ContextualSubstitutionBase::matchGlyphCoverages(lookaheadCoverageTableOffsetArray
,
494 lookaheadGlyphCount
, &tempIterator
, (const char *) this)) {
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();
504 if (ContextualSubstitutionBase::matchGlyphCoverages(inputCoverageTableOffsetArray
,
505 inputGlyphCount
, glyphIterator
, (const char *) this)) {
506 const SubstitutionLookupRecord
*substLookupRecordArray
=
507 (const SubstitutionLookupRecord
*) &lookaheadCoverageTableOffsetArray
[lookaheadGlyphCount
+ 1];
509 ContextualSubstitutionBase::applySubstitutionLookups(lookupProcessor
, substLookupRecordArray
, substCount
, glyphIterator
, fontInstance
, position
);
511 return inputGlyphCount
;
514 glyphIterator
->setCurrStreamPosition(position
);