Home | History | Annotate | Download | only in layout
      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