]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/layout/OpenTypeLayoutEngine.cpp
ICU-59180.0.1.tar.gz
[apple/icu.git] / icuSources / layout / OpenTypeLayoutEngine.cpp
index 730c79d6cb51ae54a1158af5c4a459047b48971d..948b36626336fc868eb2f3256791e5dd89724b0a 100644 (file)
@@ -1,8 +1,7 @@
 
 /*
- * %W% %W%
  *
- * (C) Copyright IBM Corp. 1998-2003 - All Rights Reserved
+ * (C) Copyright IBM Corp. and others 1998-2013 - All Rights Reserved
  *
  */
 
 #include "LELanguages.h"
 
 #include "LayoutEngine.h"
+#include "CanonShaping.h"
 #include "OpenTypeLayoutEngine.h"
 #include "ScriptAndLanguageTags.h"
+#include "CharSubstitutionFilter.h"
 
 #include "GlyphSubstitutionTables.h"
 #include "GlyphDefinitionTables.h"
 #include "GlyphPositioningTables.h"
 
+#include "LEGlyphStorage.h"
+#include "GlyphPositionAdjustments.h"
+
 #include "GDEFMarkFilter.h"
 
+#include "KernTable.h"
+
 U_NAMESPACE_BEGIN
 
-const char OpenTypeLayoutEngine::fgClassID=0;
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OpenTypeLayoutEngine)
+
+#define ccmpFeatureTag LE_CCMP_FEATURE_TAG
+#define ligaFeatureTag LE_LIGA_FEATURE_TAG
+#define cligFeatureTag LE_CLIG_FEATURE_TAG
+#define kernFeatureTag LE_KERN_FEATURE_TAG
+#define markFeatureTag LE_MARK_FEATURE_TAG
+#define mkmkFeatureTag LE_MKMK_FEATURE_TAG
+#define loclFeatureTag LE_LOCL_FEATURE_TAG
+#define caltFeatureTag LE_CALT_FEATURE_TAG
+
+#define dligFeatureTag LE_DLIG_FEATURE_TAG
+#define rligFeatureTag LE_RLIG_FEATURE_TAG
+#define paltFeatureTag LE_PALT_FEATURE_TAG
+
+#define hligFeatureTag LE_HLIG_FEATURE_TAG
+#define smcpFeatureTag LE_SMCP_FEATURE_TAG
+#define fracFeatureTag LE_FRAC_FEATURE_TAG
+#define afrcFeatureTag LE_AFRC_FEATURE_TAG
+#define zeroFeatureTag LE_ZERO_FEATURE_TAG
+#define swshFeatureTag LE_SWSH_FEATURE_TAG
+#define cswhFeatureTag LE_CSWH_FEATURE_TAG
+#define saltFeatureTag LE_SALT_FEATURE_TAG
+#define naltFeatureTag LE_NALT_FEATURE_TAG
+#define rubyFeatureTag LE_RUBY_FEATURE_TAG
+#define ss01FeatureTag LE_SS01_FEATURE_TAG
+#define ss02FeatureTag LE_SS02_FEATURE_TAG
+#define ss03FeatureTag LE_SS03_FEATURE_TAG
+#define ss04FeatureTag LE_SS04_FEATURE_TAG
+#define ss05FeatureTag LE_SS05_FEATURE_TAG
+#define ss06FeatureTag LE_SS06_FEATURE_TAG
+#define ss07FeatureTag LE_SS07_FEATURE_TAG
+
+#define ccmpFeatureMask 0x80000000UL
+#define ligaFeatureMask 0x40000000UL
+#define cligFeatureMask 0x20000000UL
+#define kernFeatureMask 0x10000000UL
+#define paltFeatureMask 0x08000000UL
+#define markFeatureMask 0x04000000UL
+#define mkmkFeatureMask 0x02000000UL
+#define loclFeatureMask 0x01000000UL
+#define caltFeatureMask 0x00800000UL
+
+#define dligFeatureMask 0x00400000UL 
+#define rligFeatureMask 0x00200000UL
+#define hligFeatureMask 0x00100000UL
+#define smcpFeatureMask 0x00080000UL
+#define fracFeatureMask 0x00040000UL
+#define afrcFeatureMask 0x00020000UL
+#define zeroFeatureMask 0x00010000UL
+#define swshFeatureMask 0x00008000UL
+#define cswhFeatureMask 0x00004000UL
+#define saltFeatureMask 0x00002000UL
+#define naltFeatureMask 0x00001000UL
+#define rubyFeatureMask 0x00000800UL
+#define ss01FeatureMask 0x00000400UL
+#define ss02FeatureMask 0x00000200UL
+#define ss03FeatureMask 0x00000100UL
+#define ss04FeatureMask 0x00000080UL
+#define ss05FeatureMask 0x00000040UL
+#define ss06FeatureMask 0x00000020UL
+#define ss07FeatureMask 0x00000010UL
+
+#define minimalFeatures     (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask)
+static const FeatureMap featureMap[] =
+{
+    {ccmpFeatureTag, ccmpFeatureMask},
+    {ligaFeatureTag, ligaFeatureMask},
+    {cligFeatureTag, cligFeatureMask}, 
+    {kernFeatureTag, kernFeatureMask},
+    {paltFeatureTag, paltFeatureMask},
+    {markFeatureTag, markFeatureMask},
+    {mkmkFeatureTag, mkmkFeatureMask},
+    {loclFeatureTag, loclFeatureMask},
+    {caltFeatureTag, caltFeatureMask},
+    {hligFeatureTag, hligFeatureMask},
+    {smcpFeatureTag, smcpFeatureMask},
+    {fracFeatureTag, fracFeatureMask},
+    {afrcFeatureTag, afrcFeatureMask},
+    {zeroFeatureTag, zeroFeatureMask},
+    {swshFeatureTag, swshFeatureMask},
+    {cswhFeatureTag, cswhFeatureMask},
+    {saltFeatureTag, saltFeatureMask},
+    {naltFeatureTag, naltFeatureMask},
+    {rubyFeatureTag, rubyFeatureMask},
+    {ss01FeatureTag, ss01FeatureMask},
+    {ss02FeatureTag, ss02FeatureMask},
+    {ss03FeatureTag, ss03FeatureMask},
+    {ss04FeatureTag, ss04FeatureMask},
+    {ss05FeatureTag, ss05FeatureMask},
+    {ss06FeatureTag, ss06FeatureMask},
+    {ss07FeatureTag, ss07FeatureMask}
+};
+
+static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
 
 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
-                        const GlyphSubstitutionTableHeader *gsubTable)
-    : LayoutEngine(fontInstance, scriptCode, languageCode), fFeatureTags(NULL), fFeatureOrder(NULL),
-      fGSUBTable(gsubTable), fSubstitutionFilter(NULL)
+                     le_int32 typoFlags, const LEReferenceTo<GlyphSubstitutionTableHeader> &gsubTable, LEErrorCode &success)
+    : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures),
+      fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE),
+      fGSUBTable(gsubTable), 
+      fGDEFTable(fontInstance, LE_GDEF_TABLE_TAG, success),
+      fGPOSTable(fontInstance, LE_GPOS_TABLE_TAG, success), fSubstitutionFilter(NULL)
 {
-    static le_uint32 gdefTableTag = LE_GDEF_TABLE_TAG;
-    static le_uint32 gposTableTag = LE_GPOS_TABLE_TAG;
-
-    fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag);
-    fGPOSTable = (const GlyphPositioningTableHeader *) getFontTable(gposTableTag);
+    applyTypoFlags();
 
     setScriptAndLanguageTags();
+
+// JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
+//    if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
+    if (!fGPOSTable.isEmpty()&& !fGPOSTable->coversScript(fGPOSTable, fScriptTag, success)) {
+      fGPOSTable.clear(); // already loaded
+    }
+}
+
+void OpenTypeLayoutEngine::applyTypoFlags() {
+    const le_int32& typoFlags = fTypoFlags;
+    const LEFontInstance *fontInstance = fFontInstance;
+
+    switch (typoFlags & (LE_SS01_FEATURE_FLAG 
+                         | LE_SS02_FEATURE_FLAG
+                         | LE_SS03_FEATURE_FLAG
+                         | LE_SS04_FEATURE_FLAG
+                         | LE_SS05_FEATURE_FLAG
+                         | LE_SS06_FEATURE_FLAG
+                         | LE_SS07_FEATURE_FLAG)) {
+        case LE_SS01_FEATURE_FLAG:
+            fFeatureMask |= ss01FeatureMask;
+            break;
+        case LE_SS02_FEATURE_FLAG:
+            fFeatureMask |= ss02FeatureMask;
+            break;
+        case LE_SS03_FEATURE_FLAG:
+            fFeatureMask |= ss03FeatureMask;
+            break;
+        case LE_SS04_FEATURE_FLAG:
+            fFeatureMask |= ss04FeatureMask;
+            break;
+        case LE_SS05_FEATURE_FLAG:
+            fFeatureMask |= ss05FeatureMask;
+            break;
+        case LE_SS06_FEATURE_FLAG:
+            fFeatureMask |= ss06FeatureMask;
+            break;
+        case LE_SS07_FEATURE_FLAG:
+            fFeatureMask |= ss07FeatureMask;
+            break;
+    }
+
+    if (typoFlags & LE_Kerning_FEATURE_FLAG) {
+      fFeatureMask |= (kernFeatureMask | paltFeatureMask); 
+      // Convenience.
+    }
+    if (typoFlags & LE_Ligatures_FEATURE_FLAG) {
+      fFeatureMask |= (ligaFeatureMask | cligFeatureMask);  
+      // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ?
+    }
+    if (typoFlags & LE_CLIG_FEATURE_FLAG) fFeatureMask |= cligFeatureMask;
+    if (typoFlags & LE_DLIG_FEATURE_FLAG) fFeatureMask |= dligFeatureMask;
+    if (typoFlags & LE_HLIG_FEATURE_FLAG) fFeatureMask |= hligFeatureMask;
+    if (typoFlags & LE_LIGA_FEATURE_FLAG) fFeatureMask |= ligaFeatureMask;
+    if (typoFlags & LE_RLIG_FEATURE_FLAG) fFeatureMask |= rligFeatureMask;
+    if (typoFlags & LE_SMCP_FEATURE_FLAG) fFeatureMask |= smcpFeatureMask;
+    if (typoFlags & LE_FRAC_FEATURE_FLAG) fFeatureMask |= fracFeatureMask;
+    if (typoFlags & LE_AFRC_FEATURE_FLAG) fFeatureMask |= afrcFeatureMask;
+    if (typoFlags & LE_ZERO_FEATURE_FLAG) fFeatureMask |= zeroFeatureMask;
+    if (typoFlags & LE_SWSH_FEATURE_FLAG) fFeatureMask |= swshFeatureMask;
+    if (typoFlags & LE_CSWH_FEATURE_FLAG) fFeatureMask |= cswhFeatureMask;
+    if (typoFlags & LE_SALT_FEATURE_FLAG) fFeatureMask |= saltFeatureMask;
+    if (typoFlags & LE_RUBY_FEATURE_FLAG) fFeatureMask |= rubyFeatureMask;
+    if (typoFlags & LE_NALT_FEATURE_FLAG) {
+      // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm
+      fFeatureMask = naltFeatureMask; 
+    }
+
+    if (typoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
+      // This isn't a font feature, but requests a Char Substitution Filter
+      fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
+    }
+
 }
 
 void OpenTypeLayoutEngine::reset()
@@ -45,26 +218,24 @@ void OpenTypeLayoutEngine::reset()
     // will have been called already by
     // LayoutEngine::~LayoutEngine()
     LayoutEngine::reset();
-
-    // The double call could be avoided by
-    // puting the following into a private
-    // method that's called from here and
-    // from our destructor
-    if (fFeatureTags != NULL) {
-        LE_DELETE_ARRAY(fFeatureTags);
-        fFeatureTags = NULL;
-    }
 }
 
-OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode)
-    : LayoutEngine(fontInstance, scriptCode, languageCode), fFeatureTags(NULL), fFeatureOrder(NULL),
-      fGSUBTable(NULL), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL)
+OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
+                       le_int32 typoFlags, LEErrorCode &success)
+    : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE),
+      fGSUBTable(), fGDEFTable(), fGPOSTable(), fSubstitutionFilter(NULL)
 {
-    setScriptAndLanguageTags();
+  applyTypoFlags();
+  setScriptAndLanguageTags();
 }
 
 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
 {
+    if (fTypoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
+        delete fSubstitutionFilter;
+        fSubstitutionFilter = NULL;
+    }
+
     reset();
 }
 
@@ -73,10 +244,25 @@ LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
     if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
         return 0xFFFFFFFF;
     }
-
     return scriptTags[scriptCode];
 }
 
+LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode)
+{
+       switch (scriptCode) { 
+               case bengScriptCode :    return bng2ScriptTag;
+               case devaScriptCode :    return dev2ScriptTag;
+               case gujrScriptCode :    return gjr2ScriptTag;
+               case guruScriptCode :    return gur2ScriptTag;
+               case kndaScriptCode :    return knd2ScriptTag;
+               case mlymScriptCode :    return mlm2ScriptTag;
+               case oryaScriptCode :    return ory2ScriptTag;
+               case tamlScriptCode :    return tml2ScriptTag;
+               case teluScriptCode :    return tel2ScriptTag;
+               default:                 return nullScriptTag;
+       }
+}
+
 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
 {
     if (languageCode < 0 || languageCode >= languageCodeCount) {
@@ -89,13 +275,61 @@ LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
 {
     fScriptTag  = getScriptTag(fScriptCode);
+    fScriptTagV2 = getV2ScriptTag(fScriptCode);
     fLangSysTag = getLangSysTag(fLanguageCode);
 }
 
+le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
+                LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success)
+{
+    if (LE_FAILURE(success)) {
+        return 0;
+    }
+
+    if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
+        success = LE_ILLEGAL_ARGUMENT_ERROR;
+        return 0;
+    }
+
+    // This is the cheapest way to get mark reordering only for Hebrew.
+    // We could just do the mark reordering for all scripts, but most
+    // of them probably don't need it... Another option would be to
+    // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
+    // would need to do is mark reordering, so that seems like overkill.
+    if (fScriptCode == hebrScriptCode) {
+        outChars = LE_NEW_ARRAY(LEUnicode, count);
+
+        if (outChars == NULL) {
+            success = LE_MEMORY_ALLOCATION_ERROR;
+            return 0;
+        }
+
+        if (LE_FAILURE(success)) {
+            LE_DELETE_ARRAY(outChars);
+            return 0;
+        }
+
+        CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage);
+    }
+
+    if (LE_FAILURE(success)) {
+        return 0;
+    }
+
+    glyphStorage.allocateGlyphArray(count, rightToLeft, success);
+    glyphStorage.allocateAuxData(success);
+
+    for (le_int32 i = 0; i < count; i += 1) {
+        glyphStorage.setAuxData(i, fFeatureMask, success);
+    }
+
+    return count;
+}
+
 // Input: characters, tags
 // Output: glyphs, char indices
-le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, const LETag **featureTags,
-                LEGlyphID *&glyphs, le_int32 *&charIndices, LEErrorCode &success)
+le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
+                                               LEGlyphStorage &glyphStorage, LEErrorCode &success)
 {
     if (LE_FAILURE(success)) {
         return 0;
@@ -106,25 +340,71 @@ le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32
         return 0;
     }
 
-    mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphs, charIndices, success);
+    mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
 
     if (LE_FAILURE(success)) {
         return 0;
     }
+    
+    if (fGSUBTable.isValid()) {
+      if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable, fScriptTagV2, fLangSysTag, success)) { 
+          count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
+                                    fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
+
+        } else {
+          count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
+                                    fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
+        }
+    }
 
-    if (fGSUBTable != NULL) {
-        fGSUBTable->process(glyphs, featureTags, count, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter, fFeatureOrder);
+    return count;
+}
+// Input: characters, tags
+// Output: glyphs, char indices
+le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft,
+                                               LEGlyphStorage &glyphStorage, LEErrorCode &success)
+{
+    if (LE_FAILURE(success)) {
+        return 0;
+    }
+
+    if ( count < 0 || max < 0 ) {
+        success = LE_ILLEGAL_ARGUMENT_ERROR;
+        return 0;
+    }
+   
+    if (fGSUBTable.isValid()) {
+       if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable,fScriptTagV2,fLangSysTag,success)) { 
+          count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
+                                    fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
+
+        } else {
+          count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
+                                    fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
+        }
     }
 
     return count;
 }
+le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success)
+{
+    if (LE_FAILURE(success)) {
+        return 0;
+    }
+
+    glyphStorage.adoptGlyphArray(tempGlyphStorage);
+    glyphStorage.adoptCharIndicesArray(tempGlyphStorage);
+    glyphStorage.adoptAuxDataArray(tempGlyphStorage);
+    glyphStorage.adoptGlyphCount(tempGlyphStorage);
+
+    return glyphStorage.getGlyphCount();
+}
 
-le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphID *&glyphs, le_int32 *&charIndices, LEErrorCode &success)
+le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success)
 {
     LEUnicode *outChars = NULL;
-    LEGlyphID *fakeGlyphs = NULL;
-    le_int32 *tempCharIndices = NULL;
-    le_int32 outCharCount, outGlyphCount, fakeGlyphCount;
+    LEGlyphStorage fakeGlyphStorage;
+    le_int32 outCharCount, outGlyphCount;
 
     if (LE_FAILURE(success)) {
         return 0;
@@ -135,48 +415,52 @@ le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 o
         return 0;
     }
 
-    outCharCount   = characterProcessing(chars, offset, count, max, rightToLeft, outChars, tempCharIndices, fFeatureTags, success);
+    outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success);
+
+    if (LE_FAILURE(success)) {
+        return 0;
+    }
 
     if (outChars != NULL) {
-        fakeGlyphCount = glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fFeatureTags, fakeGlyphs, tempCharIndices, success);
+        // le_int32 fakeGlyphCount = 
+        glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success);
+        LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
         //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
     } else {
-        fakeGlyphCount = glyphProcessing(chars, offset, count, max, rightToLeft, fFeatureTags, fakeGlyphs, tempCharIndices, success);
+        // le_int32 fakeGlyphCount =
+        glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
         //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
     }
 
-    outGlyphCount  = glyphPostProcessing(fakeGlyphs, tempCharIndices, fakeGlyphCount, glyphs, charIndices, success);
-
-    if (outChars != chars) {
-        LE_DELETE_ARRAY(outChars);
-    }
-
-    if (fakeGlyphs != glyphs) {
-        LE_DELETE_ARRAY(fakeGlyphs);
+    if (LE_FAILURE(success)) {
+        return 0;
     }
 
-    if (tempCharIndices != charIndices) {
-        LE_DELETE_ARRAY(tempCharIndices);
-    }
+    outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
 
     return outGlyphCount;
 }
 
 // apply GPOS table, if any
 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
-                                                LEGlyphID glyphs[], le_int32 glyphCount, float positions[], LEErrorCode &success)
+                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
 {
     if (LE_FAILURE(success)) {
         return;
     }
 
-    if (chars == NULL || glyphs == NULL || positions == NULL || offset < 0 || count < 0) {
+    if (chars == NULL || offset < 0 || count < 0) {
         success = LE_ILLEGAL_ARGUMENT_ERROR;
         return;
     }
 
-    if (glyphCount > 0 && fGPOSTable != NULL) {
-        GlyphPositionAdjustment *adjustments = LE_NEW_ARRAY(GlyphPositionAdjustment, glyphCount);
+    le_int32 glyphCount = glyphStorage.getGlyphCount();
+    if (glyphCount == 0) {
+        return;
+    }
+
+    if (!fGPOSTable.isEmpty()) {
+        GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
         le_int32 i;
 
         if (adjustments == NULL) {
@@ -184,23 +468,41 @@ void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int3
             return;
         }
 
+#if 0
+        // Don't need to do this if we allocate
+        // the adjustments array w/ new...
         for (i = 0; i < glyphCount; i += 1) {
-            adjustments[i].setXPlacement(0);
-            adjustments[i].setYPlacement(0);
+            adjustments->setXPlacement(i, 0);
+            adjustments->setYPlacement(i, 0);
 
-            adjustments[i].setXAdvance(0);
-            adjustments[i].setYAdvance(0);
+            adjustments->setXAdvance(i, 0);
+            adjustments->setYAdvance(i, 0);
 
-            adjustments[i].setBaseOffset(-1);
+            adjustments->setBaseOffset(i, -1);
         }
+#endif
 
-        fGPOSTable->process(glyphs, adjustments, fFeatureTags, glyphCount, reverse, fScriptTag, fLangSysTag, fGDEFTable, fFontInstance, fFeatureOrder);
+        if (!fGPOSTable.isEmpty()) {
+            if (fScriptTagV2 != nullScriptTag && 
+                fGPOSTable->coversScriptAndLanguage(fGPOSTable, fScriptTagV2,fLangSysTag,success)) { 
+              fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag, 
+                                  fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
+            } else {
+              fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, 
+                                  fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
+            }
+        } else if (fTypoFlags & LE_Kerning_FEATURE_FLAG) { /* kerning enabled */
+          LETableReference kernTable(fFontInstance, LE_KERN_TABLE_TAG, success);
+          KernTable kt(kernTable, success);
+          kt.process(glyphStorage, success);
+        }
 
         float xAdjust = 0, yAdjust = 0;
 
         for (i = 0; i < glyphCount; i += 1) {
-            float xAdvance   = adjustments[i].getXAdvance();
-            float yAdvance   = adjustments[i].getYAdvance();
+            float xAdvance   = adjustments->getXAdvance(i);
+            float yAdvance   = adjustments->getYAdvance(i);
             float xPlacement = 0;
             float yPlacement = 0;
 
@@ -212,26 +514,44 @@ void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int3
             yAdjust += yKerning;
 #endif
 
-            for (le_int32 base = i; base >= 0; base = adjustments[base].getBaseOffset()) {
-                xPlacement += adjustments[base].getXPlacement();
-                yPlacement += adjustments[base].getYPlacement();
+            for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
+                xPlacement += adjustments->getXPlacement(base);
+                yPlacement += adjustments->getYPlacement(base);
             }
 
-            positions[i*2]   += xAdjust + fFontInstance->xUnitsToPoints(xPlacement);
-            positions[i*2+1] -= yAdjust + fFontInstance->yUnitsToPoints(yPlacement);
+            xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
+            yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
+            glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
 
             xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
             yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
         }
 
-        positions[glyphCount*2]   += xAdjust;
-        positions[glyphCount*2+1] -= yAdjust;
+        glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
+
+        delete adjustments;
+    } else {
+        // if there was no GPOS table, maybe there's non-OpenType kerning we can use
+        LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success);        
+    }
+
+    LEGlyphID zwnj  = fFontInstance->mapCharToGlyph(0x200C);
+
+    if (zwnj != 0x0000) {
+        for (le_int32 g = 0; g < glyphCount; g += 1) {
+            LEGlyphID glyph = glyphStorage[g];
 
-        LE_DELETE_ARRAY(adjustments);
+            if (glyph == zwnj) {
+                glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
+            }
+        }
     }
 
+#if 0
+    // Don't know why this is here...
     LE_DELETE_ARRAY(fFeatureTags);
     fFeatureTags = NULL;
+#endif
 }
 
 U_NAMESPACE_END