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 GlyphSubstitutionTableHeader *gsubTable, LEErrorCode &success) 130 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures), 131 fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE), 132 fGSUBTable(gsubTable), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL) 133 { 134 static const le_uint32 gdefTableTag = LE_GDEF_TABLE_TAG; 135 static const le_uint32 gposTableTag = LE_GPOS_TABLE_TAG; 136 const GlyphPositioningTableHeader *gposTable = (const GlyphPositioningTableHeader *) getFontTable(gposTableTag); 137 138 switch (typoFlags & (LE_SS01_FEATURE_FLAG 139 | LE_SS02_FEATURE_FLAG 140 | LE_SS03_FEATURE_FLAG 141 | LE_SS04_FEATURE_FLAG 142 | LE_SS05_FEATURE_FLAG 143 | LE_SS06_FEATURE_FLAG 144 | LE_SS07_FEATURE_FLAG)) { 145 case LE_SS01_FEATURE_FLAG: 146 fFeatureMask |= ss01FeatureMask; 147 break; 148 case LE_SS02_FEATURE_FLAG: 149 fFeatureMask |= ss02FeatureMask; 150 break; 151 case LE_SS03_FEATURE_FLAG: 152 fFeatureMask |= ss03FeatureMask; 153 break; 154 case LE_SS04_FEATURE_FLAG: 155 fFeatureMask |= ss04FeatureMask; 156 break; 157 case LE_SS05_FEATURE_FLAG: 158 fFeatureMask |= ss05FeatureMask; 159 break; 160 case LE_SS06_FEATURE_FLAG: 161 fFeatureMask |= ss06FeatureMask; 162 break; 163 case LE_SS07_FEATURE_FLAG: 164 fFeatureMask |= ss07FeatureMask; 165 break; 166 } 167 168 if (typoFlags & LE_Kerning_FEATURE_FLAG) { 169 fFeatureMask |= (kernFeatureMask | paltFeatureMask); 170 // Convenience. 171 } 172 if (typoFlags & LE_Ligatures_FEATURE_FLAG) { 173 fFeatureMask |= (ligaFeatureMask | cligFeatureMask); 174 // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ? 175 } 176 if (typoFlags & LE_CLIG_FEATURE_FLAG) fFeatureMask |= cligFeatureMask; 177 if (typoFlags & LE_DLIG_FEATURE_FLAG) fFeatureMask |= dligFeatureMask; 178 if (typoFlags & LE_HLIG_FEATURE_FLAG) fFeatureMask |= hligFeatureMask; 179 if (typoFlags & LE_LIGA_FEATURE_FLAG) fFeatureMask |= ligaFeatureMask; 180 if (typoFlags & LE_RLIG_FEATURE_FLAG) fFeatureMask |= rligFeatureMask; 181 if (typoFlags & LE_SMCP_FEATURE_FLAG) fFeatureMask |= smcpFeatureMask; 182 if (typoFlags & LE_FRAC_FEATURE_FLAG) fFeatureMask |= fracFeatureMask; 183 if (typoFlags & LE_AFRC_FEATURE_FLAG) fFeatureMask |= afrcFeatureMask; 184 if (typoFlags & LE_ZERO_FEATURE_FLAG) fFeatureMask |= zeroFeatureMask; 185 if (typoFlags & LE_SWSH_FEATURE_FLAG) fFeatureMask |= swshFeatureMask; 186 if (typoFlags & LE_CSWH_FEATURE_FLAG) fFeatureMask |= cswhFeatureMask; 187 if (typoFlags & LE_SALT_FEATURE_FLAG) fFeatureMask |= saltFeatureMask; 188 if (typoFlags & LE_RUBY_FEATURE_FLAG) fFeatureMask |= rubyFeatureMask; 189 if (typoFlags & LE_NALT_FEATURE_FLAG) { 190 // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm 191 fFeatureMask = naltFeatureMask; 192 } 193 194 if (typoFlags & LE_CHAR_FILTER_FEATURE_FLAG) { 195 // This isn't a font feature, but requests a Char Substitution Filter 196 fSubstitutionFilter = new CharSubstitutionFilter(fontInstance); 197 } 198 199 setScriptAndLanguageTags(); 200 201 fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag); 202 203 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font 204 // if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) { 205 if (gposTable != NULL && gposTable->coversScript(fScriptTag)) { 206 fGPOSTable = gposTable; 207 } 208 } 209 210 void OpenTypeLayoutEngine::reset() 211 { 212 // NOTE: if we're called from 213 // the destructor, LayoutEngine;:reset() 214 // will have been called already by 215 // LayoutEngine::~LayoutEngine() 216 LayoutEngine::reset(); 217 } 218 219 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, 220 le_int32 typoFlags, LEErrorCode &success) 221 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE), 222 fGSUBTable(NULL), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL) 223 { 224 setScriptAndLanguageTags(); 225 } 226 227 OpenTypeLayoutEngine::~OpenTypeLayoutEngine() 228 { 229 if (fTypoFlags & 0x80000000L) { 230 delete fSubstitutionFilter; 231 } 232 233 reset(); 234 } 235 236 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode) 237 { 238 if (scriptCode < 0 || scriptCode >= scriptCodeCount) { 239 return 0xFFFFFFFF; 240 } 241 return scriptTags[scriptCode]; 242 } 243 244 LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode) 245 { 246 switch (scriptCode) { 247 case bengScriptCode : return bng2ScriptTag; 248 case devaScriptCode : return dev2ScriptTag; 249 case gujrScriptCode : return gjr2ScriptTag; 250 case guruScriptCode : return gur2ScriptTag; 251 case kndaScriptCode : return knd2ScriptTag; 252 case mlymScriptCode : return mlm2ScriptTag; 253 case oryaScriptCode : return ory2ScriptTag; 254 case tamlScriptCode : return tml2ScriptTag; 255 case teluScriptCode : return tel2ScriptTag; 256 default: return nullScriptTag; 257 } 258 } 259 260 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode) 261 { 262 if (languageCode < 0 || languageCode >= languageCodeCount) { 263 return 0xFFFFFFFF; 264 } 265 266 return languageTags[languageCode]; 267 } 268 269 void OpenTypeLayoutEngine::setScriptAndLanguageTags() 270 { 271 fScriptTag = getScriptTag(fScriptCode); 272 fScriptTagV2 = getV2ScriptTag(fScriptCode); 273 fLangSysTag = getLangSysTag(fLanguageCode); 274 } 275 276 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, 277 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) 278 { 279 if (LE_FAILURE(success)) { 280 return 0; 281 } 282 283 if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 284 success = LE_ILLEGAL_ARGUMENT_ERROR; 285 return 0; 286 } 287 288 // This is the cheapest way to get mark reordering only for Hebrew. 289 // We could just do the mark reordering for all scripts, but most 290 // of them probably don't need it... Another option would be to 291 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it 292 // would need to do is mark reordering, so that seems like overkill. 293 if (fScriptCode == hebrScriptCode) { 294 outChars = LE_NEW_ARRAY(LEUnicode, count); 295 296 if (outChars == NULL) { 297 success = LE_MEMORY_ALLOCATION_ERROR; 298 return 0; 299 } 300 301 if (LE_FAILURE(success)) { 302 LE_DELETE_ARRAY(outChars); 303 return 0; 304 } 305 306 CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage); 307 } 308 309 if (LE_FAILURE(success)) { 310 return 0; 311 } 312 313 glyphStorage.allocateGlyphArray(count, rightToLeft, success); 314 glyphStorage.allocateAuxData(success); 315 316 for (le_int32 i = 0; i < count; i += 1) { 317 glyphStorage.setAuxData(i, fFeatureMask, success); 318 } 319 320 return count; 321 } 322 323 // Input: characters, tags 324 // Output: glyphs, char indices 325 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, 326 LEGlyphStorage &glyphStorage, LEErrorCode &success) 327 { 328 if (LE_FAILURE(success)) { 329 return 0; 330 } 331 332 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 333 success = LE_ILLEGAL_ARGUMENT_ERROR; 334 return 0; 335 } 336 337 mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success); 338 339 if (LE_FAILURE(success)) { 340 return 0; 341 } 342 343 if (fGSUBTable != NULL) { 344 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) { 345 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter, 346 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 347 348 } else { 349 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter, 350 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 351 } 352 } 353 354 return count; 355 } 356 // Input: characters, tags 357 // Output: glyphs, char indices 358 le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft, 359 LEGlyphStorage &glyphStorage, LEErrorCode &success) 360 { 361 if (LE_FAILURE(success)) { 362 return 0; 363 } 364 365 if ( count < 0 || max < 0 ) { 366 success = LE_ILLEGAL_ARGUMENT_ERROR; 367 return 0; 368 } 369 370 if (fGSUBTable != NULL) { 371 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) { 372 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter, 373 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 374 375 } else { 376 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter, 377 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 378 } 379 } 380 381 return count; 382 } 383 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success) 384 { 385 if (LE_FAILURE(success)) { 386 return 0; 387 } 388 389 glyphStorage.adoptGlyphArray(tempGlyphStorage); 390 glyphStorage.adoptCharIndicesArray(tempGlyphStorage); 391 glyphStorage.adoptAuxDataArray(tempGlyphStorage); 392 glyphStorage.adoptGlyphCount(tempGlyphStorage); 393 394 return glyphStorage.getGlyphCount(); 395 } 396 397 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success) 398 { 399 LEUnicode *outChars = NULL; 400 LEGlyphStorage fakeGlyphStorage; 401 le_int32 outCharCount, outGlyphCount; 402 403 if (LE_FAILURE(success)) { 404 return 0; 405 } 406 407 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 408 success = LE_ILLEGAL_ARGUMENT_ERROR; 409 return 0; 410 } 411 412 outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success); 413 414 if (LE_FAILURE(success)) { 415 return 0; 416 } 417 418 if (outChars != NULL) { 419 // le_int32 fakeGlyphCount = 420 glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success); 421 LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work... 422 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount); 423 } else { 424 // le_int32 fakeGlyphCount = 425 glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success); 426 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount); 427 } 428 429 if (LE_FAILURE(success)) { 430 return 0; 431 } 432 433 outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success); 434 435 return outGlyphCount; 436 } 437 438 // apply GPOS table, if any 439 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, 440 LEGlyphStorage &glyphStorage, LEErrorCode &success) 441 { 442 if (LE_FAILURE(success)) { 443 return; 444 } 445 446 if (chars == NULL || offset < 0 || count < 0) { 447 success = LE_ILLEGAL_ARGUMENT_ERROR; 448 return; 449 } 450 451 le_int32 glyphCount = glyphStorage.getGlyphCount(); 452 if (glyphCount == 0) { 453 return; 454 } 455 456 if (fGPOSTable != NULL) { 457 GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount); 458 le_int32 i; 459 460 if (adjustments == NULL) { 461 success = LE_MEMORY_ALLOCATION_ERROR; 462 return; 463 } 464 465 #if 0 466 // Don't need to do this if we allocate 467 // the adjustments array w/ new... 468 for (i = 0; i < glyphCount; i += 1) { 469 adjustments->setXPlacement(i, 0); 470 adjustments->setYPlacement(i, 0); 471 472 adjustments->setXAdvance(i, 0); 473 adjustments->setYAdvance(i, 0); 474 475 adjustments->setBaseOffset(i, -1); 476 } 477 #endif 478 479 if (fGPOSTable != NULL) { 480 if (fScriptTagV2 != nullScriptTag && fGPOSTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) { 481 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag, fGDEFTable, success, fFontInstance, 482 fFeatureMap, fFeatureMapCount, fFeatureOrder); 483 484 } else { 485 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, fGDEFTable, success, fFontInstance, 486 fFeatureMap, fFeatureMapCount, fFeatureOrder); 487 } 488 } else if ( fTypoFlags & 0x1 ) { 489 static const le_uint32 kernTableTag = LE_KERN_TABLE_TAG; 490 KernTable kt(fFontInstance, getFontTable(kernTableTag)); 491 kt.process(glyphStorage); 492 } 493 494 float xAdjust = 0, yAdjust = 0; 495 496 for (i = 0; i < glyphCount; i += 1) { 497 float xAdvance = adjustments->getXAdvance(i); 498 float yAdvance = adjustments->getYAdvance(i); 499 float xPlacement = 0; 500 float yPlacement = 0; 501 502 503 #if 0 504 // This is where separate kerning adjustments 505 // should get applied. 506 xAdjust += xKerning; 507 yAdjust += yKerning; 508 #endif 509 510 for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) { 511 xPlacement += adjustments->getXPlacement(base); 512 yPlacement += adjustments->getYPlacement(base); 513 } 514 515 xPlacement = fFontInstance->xUnitsToPoints(xPlacement); 516 yPlacement = fFontInstance->yUnitsToPoints(yPlacement); 517 glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success); 518 519 xAdjust += fFontInstance->xUnitsToPoints(xAdvance); 520 yAdjust += fFontInstance->yUnitsToPoints(yAdvance); 521 } 522 523 glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success); 524 525 delete adjustments; 526 } else { 527 // if there was no GPOS table, maybe there's non-OpenType kerning we can use 528 // Google Patch: disable this. Causes problems with Tamil. 529 // Umesh says layout is poor both with and without the change, but 530 // worse with the change. See ocean/imageprocessing/layout_test_unittest.cc 531 // Public ICU ticket for this problem is #7742 532 // LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success); 533 } 534 535 LEGlyphID zwnj = fFontInstance->mapCharToGlyph(0x200C); 536 537 if (zwnj != 0x0000) { 538 for (le_int32 g = 0; g < glyphCount; g += 1) { 539 LEGlyphID glyph = glyphStorage[g]; 540 541 if (glyph == zwnj) { 542 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF); 543 } 544 } 545 } 546 547 #if 0 548 // Don't know why this is here... 549 LE_DELETE_ARRAY(fFeatureTags); 550 fFeatureTags = NULL; 551 #endif 552 } 553 554 U_NAMESPACE_END 555