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