1 2 /* 3 * 4 * (C) Copyright IBM Corp. and others 1998-2013 - All Rights Reserved 5 * 6 */ 7 8 #include "LETypes.h" 9 #include "LEScripts.h" 10 #include "LELanguages.h" 11 12 #include "LayoutEngine.h" 13 #include "CanonShaping.h" 14 #include "OpenTypeLayoutEngine.h" 15 #include "ScriptAndLanguageTags.h" 16 #include "CharSubstitutionFilter.h" 17 18 #include "GlyphSubstitutionTables.h" 19 #include "GlyphDefinitionTables.h" 20 #include "GlyphPositioningTables.h" 21 22 #include "LEGlyphStorage.h" 23 #include "GlyphPositionAdjustments.h" 24 25 #include "GDEFMarkFilter.h" 26 27 #include "KernTable.h" 28 29 U_NAMESPACE_BEGIN 30 31 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OpenTypeLayoutEngine) 32 33 #define ccmpFeatureTag LE_CCMP_FEATURE_TAG 34 #define ligaFeatureTag LE_LIGA_FEATURE_TAG 35 #define cligFeatureTag LE_CLIG_FEATURE_TAG 36 #define kernFeatureTag LE_KERN_FEATURE_TAG 37 #define markFeatureTag LE_MARK_FEATURE_TAG 38 #define mkmkFeatureTag LE_MKMK_FEATURE_TAG 39 #define loclFeatureTag LE_LOCL_FEATURE_TAG 40 #define caltFeatureTag LE_CALT_FEATURE_TAG 41 42 #define dligFeatureTag LE_DLIG_FEATURE_TAG 43 #define rligFeatureTag LE_RLIG_FEATURE_TAG 44 #define paltFeatureTag LE_PALT_FEATURE_TAG 45 46 #define hligFeatureTag LE_HLIG_FEATURE_TAG 47 #define smcpFeatureTag LE_SMCP_FEATURE_TAG 48 #define fracFeatureTag LE_FRAC_FEATURE_TAG 49 #define afrcFeatureTag LE_AFRC_FEATURE_TAG 50 #define zeroFeatureTag LE_ZERO_FEATURE_TAG 51 #define swshFeatureTag LE_SWSH_FEATURE_TAG 52 #define cswhFeatureTag LE_CSWH_FEATURE_TAG 53 #define saltFeatureTag LE_SALT_FEATURE_TAG 54 #define naltFeatureTag LE_NALT_FEATURE_TAG 55 #define rubyFeatureTag LE_RUBY_FEATURE_TAG 56 #define ss01FeatureTag LE_SS01_FEATURE_TAG 57 #define ss02FeatureTag LE_SS02_FEATURE_TAG 58 #define ss03FeatureTag LE_SS03_FEATURE_TAG 59 #define ss04FeatureTag LE_SS04_FEATURE_TAG 60 #define ss05FeatureTag LE_SS05_FEATURE_TAG 61 #define ss06FeatureTag LE_SS06_FEATURE_TAG 62 #define ss07FeatureTag LE_SS07_FEATURE_TAG 63 64 #define ccmpFeatureMask 0x80000000UL 65 #define ligaFeatureMask 0x40000000UL 66 #define cligFeatureMask 0x20000000UL 67 #define kernFeatureMask 0x10000000UL 68 #define paltFeatureMask 0x08000000UL 69 #define markFeatureMask 0x04000000UL 70 #define mkmkFeatureMask 0x02000000UL 71 #define loclFeatureMask 0x01000000UL 72 #define caltFeatureMask 0x00800000UL 73 74 #define dligFeatureMask 0x00400000UL 75 #define rligFeatureMask 0x00200000UL 76 #define hligFeatureMask 0x00100000UL 77 #define smcpFeatureMask 0x00080000UL 78 #define fracFeatureMask 0x00040000UL 79 #define afrcFeatureMask 0x00020000UL 80 #define zeroFeatureMask 0x00010000UL 81 #define swshFeatureMask 0x00008000UL 82 #define cswhFeatureMask 0x00004000UL 83 #define saltFeatureMask 0x00002000UL 84 #define naltFeatureMask 0x00001000UL 85 #define rubyFeatureMask 0x00000800UL 86 #define ss01FeatureMask 0x00000400UL 87 #define ss02FeatureMask 0x00000200UL 88 #define ss03FeatureMask 0x00000100UL 89 #define ss04FeatureMask 0x00000080UL 90 #define ss05FeatureMask 0x00000040UL 91 #define ss06FeatureMask 0x00000020UL 92 #define ss07FeatureMask 0x00000010UL 93 94 #define minimalFeatures (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask) 95 96 static const FeatureMap featureMap[] = 97 { 98 {ccmpFeatureTag, ccmpFeatureMask}, 99 {ligaFeatureTag, ligaFeatureMask}, 100 {cligFeatureTag, cligFeatureMask}, 101 {kernFeatureTag, kernFeatureMask}, 102 {paltFeatureTag, paltFeatureMask}, 103 {markFeatureTag, markFeatureMask}, 104 {mkmkFeatureTag, mkmkFeatureMask}, 105 {loclFeatureTag, loclFeatureMask}, 106 {caltFeatureTag, caltFeatureMask}, 107 {hligFeatureTag, hligFeatureMask}, 108 {smcpFeatureTag, smcpFeatureMask}, 109 {fracFeatureTag, fracFeatureMask}, 110 {afrcFeatureTag, afrcFeatureMask}, 111 {zeroFeatureTag, zeroFeatureMask}, 112 {swshFeatureTag, swshFeatureMask}, 113 {cswhFeatureTag, cswhFeatureMask}, 114 {saltFeatureTag, saltFeatureMask}, 115 {naltFeatureTag, naltFeatureMask}, 116 {rubyFeatureTag, rubyFeatureMask}, 117 {ss01FeatureTag, ss01FeatureMask}, 118 {ss02FeatureTag, ss02FeatureMask}, 119 {ss03FeatureTag, ss03FeatureMask}, 120 {ss04FeatureTag, ss04FeatureMask}, 121 {ss05FeatureTag, ss05FeatureMask}, 122 {ss06FeatureTag, ss06FeatureMask}, 123 {ss07FeatureTag, ss07FeatureMask} 124 }; 125 126 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap); 127 128 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, 129 le_int32 typoFlags, const LEReferenceTo<GlyphSubstitutionTableHeader> &gsubTable, LEErrorCode &success) 130 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures), 131 fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE), 132 fGSUBTable(gsubTable), 133 fGDEFTable(fontInstance, LE_GDEF_TABLE_TAG, success), 134 fGPOSTable(fontInstance, LE_GPOS_TABLE_TAG, success), fSubstitutionFilter(NULL) 135 { 136 applyTypoFlags(); 137 138 setScriptAndLanguageTags(); 139 140 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font 141 // if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) { 142 if (!fGPOSTable.isEmpty()&& !fGPOSTable->coversScript(fGPOSTable, fScriptTag, success)) { 143 fGPOSTable.clear(); // already loaded 144 } 145 } 146 147 void OpenTypeLayoutEngine::applyTypoFlags() { 148 const le_int32& typoFlags = fTypoFlags; 149 const LEFontInstance *fontInstance = fFontInstance; 150 151 switch (typoFlags & (LE_SS01_FEATURE_FLAG 152 | LE_SS02_FEATURE_FLAG 153 | LE_SS03_FEATURE_FLAG 154 | LE_SS04_FEATURE_FLAG 155 | LE_SS05_FEATURE_FLAG 156 | LE_SS06_FEATURE_FLAG 157 | LE_SS07_FEATURE_FLAG)) { 158 case LE_SS01_FEATURE_FLAG: 159 fFeatureMask |= ss01FeatureMask; 160 break; 161 case LE_SS02_FEATURE_FLAG: 162 fFeatureMask |= ss02FeatureMask; 163 break; 164 case LE_SS03_FEATURE_FLAG: 165 fFeatureMask |= ss03FeatureMask; 166 break; 167 case LE_SS04_FEATURE_FLAG: 168 fFeatureMask |= ss04FeatureMask; 169 break; 170 case LE_SS05_FEATURE_FLAG: 171 fFeatureMask |= ss05FeatureMask; 172 break; 173 case LE_SS06_FEATURE_FLAG: 174 fFeatureMask |= ss06FeatureMask; 175 break; 176 case LE_SS07_FEATURE_FLAG: 177 fFeatureMask |= ss07FeatureMask; 178 break; 179 } 180 181 if (typoFlags & LE_Kerning_FEATURE_FLAG) { 182 fFeatureMask |= (kernFeatureMask | paltFeatureMask); 183 // Convenience. 184 } 185 if (typoFlags & LE_Ligatures_FEATURE_FLAG) { 186 fFeatureMask |= (ligaFeatureMask | cligFeatureMask); 187 // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ? 188 } 189 if (typoFlags & LE_CLIG_FEATURE_FLAG) fFeatureMask |= cligFeatureMask; 190 if (typoFlags & LE_DLIG_FEATURE_FLAG) fFeatureMask |= dligFeatureMask; 191 if (typoFlags & LE_HLIG_FEATURE_FLAG) fFeatureMask |= hligFeatureMask; 192 if (typoFlags & LE_LIGA_FEATURE_FLAG) fFeatureMask |= ligaFeatureMask; 193 if (typoFlags & LE_RLIG_FEATURE_FLAG) fFeatureMask |= rligFeatureMask; 194 if (typoFlags & LE_SMCP_FEATURE_FLAG) fFeatureMask |= smcpFeatureMask; 195 if (typoFlags & LE_FRAC_FEATURE_FLAG) fFeatureMask |= fracFeatureMask; 196 if (typoFlags & LE_AFRC_FEATURE_FLAG) fFeatureMask |= afrcFeatureMask; 197 if (typoFlags & LE_ZERO_FEATURE_FLAG) fFeatureMask |= zeroFeatureMask; 198 if (typoFlags & LE_SWSH_FEATURE_FLAG) fFeatureMask |= swshFeatureMask; 199 if (typoFlags & LE_CSWH_FEATURE_FLAG) fFeatureMask |= cswhFeatureMask; 200 if (typoFlags & LE_SALT_FEATURE_FLAG) fFeatureMask |= saltFeatureMask; 201 if (typoFlags & LE_RUBY_FEATURE_FLAG) fFeatureMask |= rubyFeatureMask; 202 if (typoFlags & LE_NALT_FEATURE_FLAG) { 203 // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm 204 fFeatureMask = naltFeatureMask; 205 } 206 207 if (typoFlags & LE_CHAR_FILTER_FEATURE_FLAG) { 208 // This isn't a font feature, but requests a Char Substitution Filter 209 fSubstitutionFilter = new CharSubstitutionFilter(fontInstance); 210 } 211 212 } 213 214 void OpenTypeLayoutEngine::reset() 215 { 216 // NOTE: if we're called from 217 // the destructor, LayoutEngine;:reset() 218 // will have been called already by 219 // LayoutEngine::~LayoutEngine() 220 LayoutEngine::reset(); 221 } 222 223 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, 224 le_int32 typoFlags, LEErrorCode &success) 225 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE), 226 fGSUBTable(), fGDEFTable(), fGPOSTable(), fSubstitutionFilter(NULL) 227 { 228 applyTypoFlags(); 229 setScriptAndLanguageTags(); 230 } 231 232 OpenTypeLayoutEngine::~OpenTypeLayoutEngine() 233 { 234 if (fTypoFlags & LE_CHAR_FILTER_FEATURE_FLAG) { 235 delete fSubstitutionFilter; 236 fSubstitutionFilter = NULL; 237 } 238 239 reset(); 240 } 241 242 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode) 243 { 244 if (scriptCode < 0 || scriptCode >= scriptCodeCount) { 245 return 0xFFFFFFFF; 246 } 247 return scriptTags[scriptCode]; 248 } 249 250 LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode) 251 { 252 switch (scriptCode) { 253 case bengScriptCode : return bng2ScriptTag; 254 case devaScriptCode : return dev2ScriptTag; 255 case gujrScriptCode : return gjr2ScriptTag; 256 case guruScriptCode : return gur2ScriptTag; 257 case kndaScriptCode : return knd2ScriptTag; 258 case mlymScriptCode : return mlm2ScriptTag; 259 case oryaScriptCode : return ory2ScriptTag; 260 case tamlScriptCode : return tml2ScriptTag; 261 case teluScriptCode : return tel2ScriptTag; 262 default: return nullScriptTag; 263 } 264 } 265 266 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode) 267 { 268 if (languageCode < 0 || languageCode >= languageCodeCount) { 269 return 0xFFFFFFFF; 270 } 271 272 return languageTags[languageCode]; 273 } 274 275 void OpenTypeLayoutEngine::setScriptAndLanguageTags() 276 { 277 fScriptTag = getScriptTag(fScriptCode); 278 fScriptTagV2 = getV2ScriptTag(fScriptCode); 279 fLangSysTag = getLangSysTag(fLanguageCode); 280 } 281 282 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, 283 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) 284 { 285 if (LE_FAILURE(success)) { 286 return 0; 287 } 288 289 if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 290 success = LE_ILLEGAL_ARGUMENT_ERROR; 291 return 0; 292 } 293 294 // This is the cheapest way to get mark reordering only for Hebrew. 295 // We could just do the mark reordering for all scripts, but most 296 // of them probably don't need it... Another option would be to 297 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it 298 // would need to do is mark reordering, so that seems like overkill. 299 if (fScriptCode == hebrScriptCode) { 300 outChars = LE_NEW_ARRAY(LEUnicode, count); 301 302 if (outChars == NULL) { 303 success = LE_MEMORY_ALLOCATION_ERROR; 304 return 0; 305 } 306 307 if (LE_FAILURE(success)) { 308 LE_DELETE_ARRAY(outChars); 309 return 0; 310 } 311 312 CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage); 313 } 314 315 if (LE_FAILURE(success)) { 316 return 0; 317 } 318 319 glyphStorage.allocateGlyphArray(count, rightToLeft, success); 320 glyphStorage.allocateAuxData(success); 321 322 for (le_int32 i = 0; i < count; i += 1) { 323 glyphStorage.setAuxData(i, fFeatureMask, success); 324 } 325 326 return count; 327 } 328 329 // Input: characters, tags 330 // Output: glyphs, char indices 331 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, 332 LEGlyphStorage &glyphStorage, LEErrorCode &success) 333 { 334 if (LE_FAILURE(success)) { 335 return 0; 336 } 337 338 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 339 success = LE_ILLEGAL_ARGUMENT_ERROR; 340 return 0; 341 } 342 343 mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success); 344 345 if (LE_FAILURE(success)) { 346 return 0; 347 } 348 349 if (fGSUBTable.isValid()) { 350 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable, fScriptTagV2, fLangSysTag, success)) { 351 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter, 352 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 353 354 } else { 355 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter, 356 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 357 } 358 } 359 360 return count; 361 } 362 // Input: characters, tags 363 // Output: glyphs, char indices 364 le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft, 365 LEGlyphStorage &glyphStorage, LEErrorCode &success) 366 { 367 if (LE_FAILURE(success)) { 368 return 0; 369 } 370 371 if ( count < 0 || max < 0 ) { 372 success = LE_ILLEGAL_ARGUMENT_ERROR; 373 return 0; 374 } 375 376 if (fGSUBTable.isValid()) { 377 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable,fScriptTagV2,fLangSysTag,success)) { 378 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter, 379 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 380 381 } else { 382 count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter, 383 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 384 } 385 } 386 387 return count; 388 } 389 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success) 390 { 391 if (LE_FAILURE(success)) { 392 return 0; 393 } 394 395 glyphStorage.adoptGlyphArray(tempGlyphStorage); 396 glyphStorage.adoptCharIndicesArray(tempGlyphStorage); 397 glyphStorage.adoptAuxDataArray(tempGlyphStorage); 398 glyphStorage.adoptGlyphCount(tempGlyphStorage); 399 400 return glyphStorage.getGlyphCount(); 401 } 402 403 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success) 404 { 405 LEUnicode *outChars = NULL; 406 LEGlyphStorage fakeGlyphStorage; 407 le_int32 outCharCount, outGlyphCount; 408 409 if (LE_FAILURE(success)) { 410 return 0; 411 } 412 413 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 414 success = LE_ILLEGAL_ARGUMENT_ERROR; 415 return 0; 416 } 417 418 outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success); 419 420 if (LE_FAILURE(success)) { 421 return 0; 422 } 423 424 if (outChars != NULL) { 425 // le_int32 fakeGlyphCount = 426 glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success); 427 LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work... 428 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount); 429 } else { 430 // le_int32 fakeGlyphCount = 431 glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success); 432 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount); 433 } 434 435 if (LE_FAILURE(success)) { 436 return 0; 437 } 438 439 outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success); 440 441 return outGlyphCount; 442 } 443 444 // apply GPOS table, if any 445 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, 446 LEGlyphStorage &glyphStorage, LEErrorCode &success) 447 { 448 if (LE_FAILURE(success)) { 449 return; 450 } 451 452 if (chars == NULL || offset < 0 || count < 0) { 453 success = LE_ILLEGAL_ARGUMENT_ERROR; 454 return; 455 } 456 457 le_int32 glyphCount = glyphStorage.getGlyphCount(); 458 if (glyphCount == 0) { 459 return; 460 } 461 462 if (!fGPOSTable.isEmpty()) { 463 GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount); 464 le_int32 i; 465 466 if (adjustments == NULL) { 467 success = LE_MEMORY_ALLOCATION_ERROR; 468 return; 469 } 470 471 #if 0 472 // Don't need to do this if we allocate 473 // the adjustments array w/ new... 474 for (i = 0; i < glyphCount; i += 1) { 475 adjustments->setXPlacement(i, 0); 476 adjustments->setYPlacement(i, 0); 477 478 adjustments->setXAdvance(i, 0); 479 adjustments->setYAdvance(i, 0); 480 481 adjustments->setBaseOffset(i, -1); 482 } 483 #endif 484 485 if (!fGPOSTable.isEmpty()) { 486 if (fScriptTagV2 != nullScriptTag && 487 fGPOSTable->coversScriptAndLanguage(fGPOSTable, fScriptTagV2,fLangSysTag,success)) { 488 fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag, 489 fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder); 490 491 } else { 492 fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, 493 fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder); 494 } 495 } else if (fTypoFlags & LE_Kerning_FEATURE_FLAG) { /* kerning enabled */ 496 LETableReference kernTable(fFontInstance, LE_KERN_TABLE_TAG, success); 497 KernTable kt(kernTable, success); 498 kt.process(glyphStorage, success); 499 } 500 501 float xAdjust = 0, yAdjust = 0; 502 503 for (i = 0; i < glyphCount; i += 1) { 504 float xAdvance = adjustments->getXAdvance(i); 505 float yAdvance = adjustments->getYAdvance(i); 506 float xPlacement = 0; 507 float yPlacement = 0; 508 509 510 #if 0 511 // This is where separate kerning adjustments 512 // should get applied. 513 xAdjust += xKerning; 514 yAdjust += yKerning; 515 #endif 516 517 for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) { 518 xPlacement += adjustments->getXPlacement(base); 519 yPlacement += adjustments->getYPlacement(base); 520 } 521 522 xPlacement = fFontInstance->xUnitsToPoints(xPlacement); 523 yPlacement = fFontInstance->yUnitsToPoints(yPlacement); 524 glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success); 525 526 xAdjust += fFontInstance->xUnitsToPoints(xAdvance); 527 yAdjust += fFontInstance->yUnitsToPoints(yAdvance); 528 } 529 530 glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success); 531 532 delete adjustments; 533 } else { 534 // if there was no GPOS table, maybe there's non-OpenType kerning we can use 535 // Google Patch: disable this. Causes problems with Tamil. 536 // Umesh says layout is poor both with and without the change, but 537 // worse with the change. See ocean/imageprocessing/layout_test_unittest.cc 538 // Public ICU ticket for this problem is #7742 539 // LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success); 540 } 541 542 LEGlyphID zwnj = fFontInstance->mapCharToGlyph(0x200C); 543 544 if (zwnj != 0x0000) { 545 for (le_int32 g = 0; g < glyphCount; g += 1) { 546 LEGlyphID glyph = glyphStorage[g]; 547 548 if (glyph == zwnj) { 549 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF); 550 } 551 } 552 } 553 554 #if 0 555 // Don't know why this is here... 556 LE_DELETE_ARRAY(fFeatureTags); 557 fFeatureTags = NULL; 558 #endif 559 } 560 561 U_NAMESPACE_END 562