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