1 2 /* 3 * 4 * (C) Copyright IBM Corp. 1998-2012 - 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 // 'dlig' not used at the moment 43 #define dligFeatureTag 0x646C6967 44 45 // 'palt' 46 #define paltFeatureTag 0x70616C74 47 48 #define ccmpFeatureMask 0x80000000UL 49 #define ligaFeatureMask 0x40000000UL 50 #define cligFeatureMask 0x20000000UL 51 #define kernFeatureMask 0x10000000UL 52 #define paltFeatureMask 0x08000000UL 53 #define markFeatureMask 0x04000000UL 54 #define mkmkFeatureMask 0x02000000UL 55 #define loclFeatureMask 0x01000000UL 56 #define caltFeatureMask 0x00800000UL 57 58 #define minimalFeatures (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask) 59 #define ligaFeatures (ligaFeatureMask | cligFeatureMask | minimalFeatures) 60 #define kernFeatures (kernFeatureMask | paltFeatureMask | minimalFeatures) 61 #define kernAndLigaFeatures (ligaFeatures | kernFeatures) 62 63 static const FeatureMap featureMap[] = 64 { 65 {ccmpFeatureTag, ccmpFeatureMask}, 66 {ligaFeatureTag, ligaFeatureMask}, 67 {cligFeatureTag, cligFeatureMask}, 68 {kernFeatureTag, kernFeatureMask}, 69 {paltFeatureTag, paltFeatureMask}, 70 {markFeatureTag, markFeatureMask}, 71 {mkmkFeatureTag, mkmkFeatureMask}, 72 {loclFeatureTag, loclFeatureMask}, 73 {caltFeatureTag, caltFeatureMask} 74 }; 75 76 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap); 77 78 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, 79 le_int32 typoFlags, const GlyphSubstitutionTableHeader *gsubTable, LEErrorCode &success) 80 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures), 81 fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE), 82 fGSUBTable(gsubTable), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL) 83 { 84 static const le_uint32 gdefTableTag = LE_GDEF_TABLE_TAG; 85 static const le_uint32 gposTableTag = LE_GPOS_TABLE_TAG; 86 const GlyphPositioningTableHeader *gposTable = (const GlyphPositioningTableHeader *) getFontTable(gposTableTag); 87 88 // todo: switch to more flags and bitfield rather than list of feature tags? 89 switch (typoFlags & ~0x80000000L) { 90 case 0: break; // default 91 case 1: fFeatureMask = kernFeatures; break; 92 case 2: fFeatureMask = ligaFeatures; break; 93 case 3: fFeatureMask = kernAndLigaFeatures; break; 94 default: break; 95 } 96 97 if (typoFlags & 0x80000000L) { 98 fSubstitutionFilter = new CharSubstitutionFilter(fontInstance); 99 } 100 101 setScriptAndLanguageTags(); 102 103 fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag); 104 105 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font 106 // if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) { 107 if (gposTable != NULL && gposTable->coversScript(fScriptTag)) { 108 fGPOSTable = gposTable; 109 } 110 } 111 112 void OpenTypeLayoutEngine::reset() 113 { 114 // NOTE: if we're called from 115 // the destructor, LayoutEngine;:reset() 116 // will have been called already by 117 // LayoutEngine::~LayoutEngine() 118 LayoutEngine::reset(); 119 } 120 121 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, 122 le_int32 typoFlags, LEErrorCode &success) 123 : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE), 124 fGSUBTable(NULL), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL) 125 { 126 setScriptAndLanguageTags(); 127 } 128 129 OpenTypeLayoutEngine::~OpenTypeLayoutEngine() 130 { 131 if (fTypoFlags & 0x80000000L) { 132 delete fSubstitutionFilter; 133 } 134 135 reset(); 136 } 137 138 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode) 139 { 140 if (scriptCode < 0 || scriptCode >= scriptCodeCount) { 141 return 0xFFFFFFFF; 142 } 143 return scriptTags[scriptCode]; 144 } 145 146 LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode) 147 { 148 switch (scriptCode) { 149 case bengScriptCode : return bng2ScriptTag; 150 case devaScriptCode : return dev2ScriptTag; 151 case gujrScriptCode : return gjr2ScriptTag; 152 case guruScriptCode : return gur2ScriptTag; 153 case kndaScriptCode : return knd2ScriptTag; 154 case mlymScriptCode : return mlm2ScriptTag; 155 case oryaScriptCode : return ory2ScriptTag; 156 case tamlScriptCode : return tml2ScriptTag; 157 case teluScriptCode : return tel2ScriptTag; 158 default: return nullScriptTag; 159 } 160 } 161 162 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode) 163 { 164 if (languageCode < 0 || languageCode >= languageCodeCount) { 165 return 0xFFFFFFFF; 166 } 167 168 return languageTags[languageCode]; 169 } 170 171 void OpenTypeLayoutEngine::setScriptAndLanguageTags() 172 { 173 fScriptTag = getScriptTag(fScriptCode); 174 fScriptTagV2 = getV2ScriptTag(fScriptCode); 175 fLangSysTag = getLangSysTag(fLanguageCode); 176 } 177 178 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, 179 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) 180 { 181 if (LE_FAILURE(success)) { 182 return 0; 183 } 184 185 if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 186 success = LE_ILLEGAL_ARGUMENT_ERROR; 187 return 0; 188 } 189 190 // This is the cheapest way to get mark reordering only for Hebrew. 191 // We could just do the mark reordering for all scripts, but most 192 // of them probably don't need it... Another option would be to 193 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it 194 // would need to do is mark reordering, so that seems like overkill. 195 if (fScriptCode == hebrScriptCode) { 196 outChars = LE_NEW_ARRAY(LEUnicode, count); 197 198 if (outChars == NULL) { 199 success = LE_MEMORY_ALLOCATION_ERROR; 200 return 0; 201 } 202 203 if (LE_FAILURE(success)) { 204 LE_DELETE_ARRAY(outChars); 205 return 0; 206 } 207 208 CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage); 209 } 210 211 if (LE_FAILURE(success)) { 212 return 0; 213 } 214 215 glyphStorage.allocateGlyphArray(count, rightToLeft, success); 216 glyphStorage.allocateAuxData(success); 217 218 for (le_int32 i = 0; i < count; i += 1) { 219 glyphStorage.setAuxData(i, fFeatureMask, success); 220 } 221 222 return count; 223 } 224 225 // Input: characters, tags 226 // Output: glyphs, char indices 227 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, 228 LEGlyphStorage &glyphStorage, LEErrorCode &success) 229 { 230 if (LE_FAILURE(success)) { 231 return 0; 232 } 233 234 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 235 success = LE_ILLEGAL_ARGUMENT_ERROR; 236 return 0; 237 } 238 239 mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success); 240 241 if (LE_FAILURE(success)) { 242 return 0; 243 } 244 245 if (fGSUBTable != NULL) { 246 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) { 247 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter, 248 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 249 250 } else { 251 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter, 252 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 253 } 254 } 255 256 return count; 257 } 258 // Input: characters, tags 259 // Output: glyphs, char indices 260 le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft, 261 LEGlyphStorage &glyphStorage, LEErrorCode &success) 262 { 263 if (LE_FAILURE(success)) { 264 return 0; 265 } 266 267 if ( count < 0 || max < 0 ) { 268 success = LE_ILLEGAL_ARGUMENT_ERROR; 269 return 0; 270 } 271 272 if (fGSUBTable != NULL) { 273 if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) { 274 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter, 275 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 276 277 } else { 278 count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter, 279 fFeatureMap, fFeatureMapCount, fFeatureOrder, success); 280 } 281 } 282 283 return count; 284 } 285 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success) 286 { 287 if (LE_FAILURE(success)) { 288 return 0; 289 } 290 291 glyphStorage.adoptGlyphArray(tempGlyphStorage); 292 glyphStorage.adoptCharIndicesArray(tempGlyphStorage); 293 glyphStorage.adoptAuxDataArray(tempGlyphStorage); 294 glyphStorage.adoptGlyphCount(tempGlyphStorage); 295 296 return glyphStorage.getGlyphCount(); 297 } 298 299 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success) 300 { 301 LEUnicode *outChars = NULL; 302 LEGlyphStorage fakeGlyphStorage; 303 le_int32 outCharCount, outGlyphCount; 304 305 if (LE_FAILURE(success)) { 306 return 0; 307 } 308 309 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 310 success = LE_ILLEGAL_ARGUMENT_ERROR; 311 return 0; 312 } 313 314 outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success); 315 316 if (LE_FAILURE(success)) { 317 return 0; 318 } 319 320 if (outChars != NULL) { 321 // le_int32 fakeGlyphCount = 322 glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success); 323 LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work... 324 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount); 325 } else { 326 // le_int32 fakeGlyphCount = 327 glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success); 328 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount); 329 } 330 331 if (LE_FAILURE(success)) { 332 return 0; 333 } 334 335 outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success); 336 337 return outGlyphCount; 338 } 339 340 // apply GPOS table, if any 341 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, 342 LEGlyphStorage &glyphStorage, LEErrorCode &success) 343 { 344 if (LE_FAILURE(success)) { 345 return; 346 } 347 348 if (chars == NULL || offset < 0 || count < 0) { 349 success = LE_ILLEGAL_ARGUMENT_ERROR; 350 return; 351 } 352 353 le_int32 glyphCount = glyphStorage.getGlyphCount(); 354 if (glyphCount == 0) { 355 return; 356 } 357 358 if (fGPOSTable != NULL) { 359 GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount); 360 le_int32 i; 361 362 if (adjustments == NULL) { 363 success = LE_MEMORY_ALLOCATION_ERROR; 364 return; 365 } 366 367 #if 0 368 // Don't need to do this if we allocate 369 // the adjustments array w/ new... 370 for (i = 0; i < glyphCount; i += 1) { 371 adjustments->setXPlacement(i, 0); 372 adjustments->setYPlacement(i, 0); 373 374 adjustments->setXAdvance(i, 0); 375 adjustments->setYAdvance(i, 0); 376 377 adjustments->setBaseOffset(i, -1); 378 } 379 #endif 380 381 if (fGPOSTable != NULL) { 382 if (fScriptTagV2 != nullScriptTag && fGPOSTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) { 383 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag, fGDEFTable, success, fFontInstance, 384 fFeatureMap, fFeatureMapCount, fFeatureOrder); 385 386 } else { 387 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, fGDEFTable, success, fFontInstance, 388 fFeatureMap, fFeatureMapCount, fFeatureOrder); 389 } 390 } else if ( fTypoFlags & 0x1 ) { 391 static const le_uint32 kernTableTag = LE_KERN_TABLE_TAG; 392 KernTable kt(fFontInstance, getFontTable(kernTableTag)); 393 kt.process(glyphStorage); 394 } 395 396 float xAdjust = 0, yAdjust = 0; 397 398 for (i = 0; i < glyphCount; i += 1) { 399 float xAdvance = adjustments->getXAdvance(i); 400 float yAdvance = adjustments->getYAdvance(i); 401 float xPlacement = 0; 402 float yPlacement = 0; 403 404 405 #if 0 406 // This is where separate kerning adjustments 407 // should get applied. 408 xAdjust += xKerning; 409 yAdjust += yKerning; 410 #endif 411 412 for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) { 413 xPlacement += adjustments->getXPlacement(base); 414 yPlacement += adjustments->getYPlacement(base); 415 } 416 417 xPlacement = fFontInstance->xUnitsToPoints(xPlacement); 418 yPlacement = fFontInstance->yUnitsToPoints(yPlacement); 419 glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success); 420 421 xAdjust += fFontInstance->xUnitsToPoints(xAdvance); 422 yAdjust += fFontInstance->yUnitsToPoints(yAdvance); 423 } 424 425 glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success); 426 427 delete adjustments; 428 } else { 429 // if there was no GPOS table, maybe there's non-OpenType kerning we can use 430 LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success); 431 } 432 433 LEGlyphID zwnj = fFontInstance->mapCharToGlyph(0x200C); 434 435 if (zwnj != 0x0000) { 436 for (le_int32 g = 0; g < glyphCount; g += 1) { 437 LEGlyphID glyph = glyphStorage[g]; 438 439 if (glyph == zwnj) { 440 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF); 441 } 442 } 443 } 444 445 #if 0 446 // Don't know why this is here... 447 LE_DELETE_ARRAY(fFeatureTags); 448 fFeatureTags = NULL; 449 #endif 450 } 451 452 U_NAMESPACE_END 453