1 /* 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. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * 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 */ 48 void 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 69 le_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 98 le_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 137 le_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 167 le_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 201 le_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 244 le_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 290 le_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 317 le_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... 355 static const FeatureMask emptyFeatureList = 0x00000000UL; 356 357 le_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 422 le_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 496 le_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 }