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 29U_NAMESPACE_BEGIN 30 31UOBJECT_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 63static 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 76static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap); 77 78OpenTypeLayoutEngine::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 112void 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 121OpenTypeLayoutEngine::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 129OpenTypeLayoutEngine::~OpenTypeLayoutEngine() 130{ 131 if (fTypoFlags & 0x80000000L) { 132 delete fSubstitutionFilter; 133 } 134 135 reset(); 136} 137 138LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode) 139{ 140 if (scriptCode < 0 || scriptCode >= scriptCodeCount) { 141 return 0xFFFFFFFF; 142 } 143 return scriptTags[scriptCode]; 144} 145 146LETag 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 162LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode) 163{ 164 if (languageCode < 0 || languageCode >= languageCodeCount) { 165 return 0xFFFFFFFF; 166 } 167 168 return languageTags[languageCode]; 169} 170 171void OpenTypeLayoutEngine::setScriptAndLanguageTags() 172{ 173 fScriptTag = getScriptTag(fScriptCode); 174 fScriptTagV2 = getV2ScriptTag(fScriptCode); 175 fLangSysTag = getLangSysTag(fLanguageCode); 176} 177 178le_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 227le_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 260le_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} 285le_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 299le_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 339void 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 450U_NAMESPACE_END 451