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