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