1 2 /* 3 * 4 * (C) Copyright IBM Corp. 1998-2010 - 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, fakeGlyphCount; 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 fakeGlyphCount = glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success); 322 LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work... 323 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount); 324 } else { 325 fakeGlyphCount = glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success); 326 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount); 327 } 328 329 if (LE_FAILURE(success)) { 330 return 0; 331 } 332 333 outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success); 334 335 return outGlyphCount; 336 } 337 338 // apply GPOS table, if any 339 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, 340 LEGlyphStorage &glyphStorage, LEErrorCode &success) 341 { 342 if (LE_FAILURE(success)) { 343 return; 344 } 345 346 if (chars == NULL || offset < 0 || count < 0) { 347 success = LE_ILLEGAL_ARGUMENT_ERROR; 348 return; 349 } 350 351 le_int32 glyphCount = glyphStorage.getGlyphCount(); 352 if (glyphCount == 0) { 353 return; 354 } 355 356 if (fGPOSTable != NULL) { 357 GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount); 358 le_int32 i; 359 360 if (adjustments == NULL) { 361 success = LE_MEMORY_ALLOCATION_ERROR; 362 return; 363 } 364 365 #if 0 366 // Don't need to do this if we allocate 367 // the adjustments array w/ new... 368 for (i = 0; i < glyphCount; i += 1) { 369 adjustments->setXPlacement(i, 0); 370 adjustments->setYPlacement(i, 0); 371 372 adjustments->setXAdvance(i, 0); 373 adjustments->setYAdvance(i, 0); 374 375 adjustments->setBaseOffset(i, -1); 376 } 377 #endif 378 379 if (fGPOSTable != NULL) { 380 if (fScriptTagV2 != nullScriptTag && fGPOSTable->coversScriptAndLanguage(fScriptTagV2,fLangSysTag)) { 381 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag, fGDEFTable, success, fFontInstance, 382 fFeatureMap, fFeatureMapCount, fFeatureOrder); 383 384 } else { 385 fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, fGDEFTable, success, fFontInstance, 386 fFeatureMap, fFeatureMapCount, fFeatureOrder); 387 } 388 } else if ( fTypoFlags & 0x1 ) { 389 static const le_uint32 kernTableTag = LE_KERN_TABLE_TAG; 390 KernTable kt(fFontInstance, getFontTable(kernTableTag)); 391 kt.process(glyphStorage); 392 } 393 394 float xAdjust = 0, yAdjust = 0; 395 396 for (i = 0; i < glyphCount; i += 1) { 397 float xAdvance = adjustments->getXAdvance(i); 398 float yAdvance = adjustments->getYAdvance(i); 399 float xPlacement = 0; 400 float yPlacement = 0; 401 402 403 #if 0 404 // This is where separate kerning adjustments 405 // should get applied. 406 xAdjust += xKerning; 407 yAdjust += yKerning; 408 #endif 409 410 for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) { 411 xPlacement += adjustments->getXPlacement(base); 412 yPlacement += adjustments->getYPlacement(base); 413 } 414 415 xPlacement = fFontInstance->xUnitsToPoints(xPlacement); 416 yPlacement = fFontInstance->yUnitsToPoints(yPlacement); 417 glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success); 418 419 xAdjust += fFontInstance->xUnitsToPoints(xAdvance); 420 yAdjust += fFontInstance->yUnitsToPoints(yAdvance); 421 } 422 423 glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success); 424 425 delete adjustments; 426 } else { 427 // if there was no GPOS table, maybe there's non-OpenType kerning we can use 428 LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success); 429 } 430 431 LEGlyphID zwnj = fFontInstance->mapCharToGlyph(0x200C); 432 433 if (zwnj != 0x0000) { 434 for (le_int32 g = 0; g < glyphCount; g += 1) { 435 LEGlyphID glyph = glyphStorage[g]; 436 437 if (glyph == zwnj) { 438 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF); 439 } 440 } 441 } 442 443 #if 0 444 // Don't know why this is here... 445 LE_DELETE_ARRAY(fFeatureTags); 446 fFeatureTags = NULL; 447 #endif 448 } 449 450 U_NAMESPACE_END 451