• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  *
4  * (C) Copyright IBM Corp. and others 1998-2013 - 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 #define dligFeatureTag LE_DLIG_FEATURE_TAG
43 #define rligFeatureTag LE_RLIG_FEATURE_TAG
44 #define paltFeatureTag LE_PALT_FEATURE_TAG
45 
46 #define hligFeatureTag LE_HLIG_FEATURE_TAG
47 #define smcpFeatureTag LE_SMCP_FEATURE_TAG
48 #define fracFeatureTag LE_FRAC_FEATURE_TAG
49 #define afrcFeatureTag LE_AFRC_FEATURE_TAG
50 #define zeroFeatureTag LE_ZERO_FEATURE_TAG
51 #define swshFeatureTag LE_SWSH_FEATURE_TAG
52 #define cswhFeatureTag LE_CSWH_FEATURE_TAG
53 #define saltFeatureTag LE_SALT_FEATURE_TAG
54 #define naltFeatureTag LE_NALT_FEATURE_TAG
55 #define rubyFeatureTag LE_RUBY_FEATURE_TAG
56 #define ss01FeatureTag LE_SS01_FEATURE_TAG
57 #define ss02FeatureTag LE_SS02_FEATURE_TAG
58 #define ss03FeatureTag LE_SS03_FEATURE_TAG
59 #define ss04FeatureTag LE_SS04_FEATURE_TAG
60 #define ss05FeatureTag LE_SS05_FEATURE_TAG
61 #define ss06FeatureTag LE_SS06_FEATURE_TAG
62 #define ss07FeatureTag LE_SS07_FEATURE_TAG
63 
64 #define ccmpFeatureMask 0x80000000UL
65 #define ligaFeatureMask 0x40000000UL
66 #define cligFeatureMask 0x20000000UL
67 #define kernFeatureMask 0x10000000UL
68 #define paltFeatureMask 0x08000000UL
69 #define markFeatureMask 0x04000000UL
70 #define mkmkFeatureMask 0x02000000UL
71 #define loclFeatureMask 0x01000000UL
72 #define caltFeatureMask 0x00800000UL
73 
74 #define dligFeatureMask 0x00400000UL
75 #define rligFeatureMask 0x00200000UL
76 #define hligFeatureMask 0x00100000UL
77 #define smcpFeatureMask 0x00080000UL
78 #define fracFeatureMask 0x00040000UL
79 #define afrcFeatureMask 0x00020000UL
80 #define zeroFeatureMask 0x00010000UL
81 #define swshFeatureMask 0x00008000UL
82 #define cswhFeatureMask 0x00004000UL
83 #define saltFeatureMask 0x00002000UL
84 #define naltFeatureMask 0x00001000UL
85 #define rubyFeatureMask 0x00000800UL
86 #define ss01FeatureMask 0x00000400UL
87 #define ss02FeatureMask 0x00000200UL
88 #define ss03FeatureMask 0x00000100UL
89 #define ss04FeatureMask 0x00000080UL
90 #define ss05FeatureMask 0x00000040UL
91 #define ss06FeatureMask 0x00000020UL
92 #define ss07FeatureMask 0x00000010UL
93 
94 #define minimalFeatures     (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask)
95 
96 static const FeatureMap featureMap[] =
97 {
98     {ccmpFeatureTag, ccmpFeatureMask},
99     {ligaFeatureTag, ligaFeatureMask},
100     {cligFeatureTag, cligFeatureMask},
101     {kernFeatureTag, kernFeatureMask},
102     {paltFeatureTag, paltFeatureMask},
103     {markFeatureTag, markFeatureMask},
104     {mkmkFeatureTag, mkmkFeatureMask},
105     {loclFeatureTag, loclFeatureMask},
106     {caltFeatureTag, caltFeatureMask},
107     {hligFeatureTag, hligFeatureMask},
108     {smcpFeatureTag, smcpFeatureMask},
109     {fracFeatureTag, fracFeatureMask},
110     {afrcFeatureTag, afrcFeatureMask},
111     {zeroFeatureTag, zeroFeatureMask},
112     {swshFeatureTag, swshFeatureMask},
113     {cswhFeatureTag, cswhFeatureMask},
114     {saltFeatureTag, saltFeatureMask},
115     {naltFeatureTag, naltFeatureMask},
116     {rubyFeatureTag, rubyFeatureMask},
117     {ss01FeatureTag, ss01FeatureMask},
118     {ss02FeatureTag, ss02FeatureMask},
119     {ss03FeatureTag, ss03FeatureMask},
120     {ss04FeatureTag, ss04FeatureMask},
121     {ss05FeatureTag, ss05FeatureMask},
122     {ss06FeatureTag, ss06FeatureMask},
123     {ss07FeatureTag, ss07FeatureMask}
124 };
125 
126 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
127 
OpenTypeLayoutEngine(const LEFontInstance * fontInstance,le_int32 scriptCode,le_int32 languageCode,le_int32 typoFlags,const LEReferenceTo<GlyphSubstitutionTableHeader> & gsubTable,LEErrorCode & success)128 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
129                      le_int32 typoFlags, const LEReferenceTo<GlyphSubstitutionTableHeader> &gsubTable, LEErrorCode &success)
130     : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureMask(minimalFeatures),
131       fFeatureMap(featureMap), fFeatureMapCount(featureMapCount), fFeatureOrder(FALSE),
132       fGSUBTable(gsubTable),
133       fGDEFTable(fontInstance, LE_GDEF_TABLE_TAG, success),
134       fGPOSTable(fontInstance, LE_GPOS_TABLE_TAG, success), fSubstitutionFilter(NULL)
135 {
136     applyTypoFlags();
137 
138     setScriptAndLanguageTags();
139 
140 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
141 //    if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
142     if (!fGPOSTable.isEmpty()&& !fGPOSTable->coversScript(fGPOSTable, fScriptTag, success)) {
143       fGPOSTable.clear(); // already loaded
144     }
145 }
146 
applyTypoFlags()147 void OpenTypeLayoutEngine::applyTypoFlags() {
148     const le_int32& typoFlags = fTypoFlags;
149     const LEFontInstance *fontInstance = fFontInstance;
150 
151     switch (typoFlags & (LE_SS01_FEATURE_FLAG
152                          | LE_SS02_FEATURE_FLAG
153                          | LE_SS03_FEATURE_FLAG
154                          | LE_SS04_FEATURE_FLAG
155                          | LE_SS05_FEATURE_FLAG
156                          | LE_SS06_FEATURE_FLAG
157                          | LE_SS07_FEATURE_FLAG)) {
158         case LE_SS01_FEATURE_FLAG:
159             fFeatureMask |= ss01FeatureMask;
160             break;
161         case LE_SS02_FEATURE_FLAG:
162             fFeatureMask |= ss02FeatureMask;
163             break;
164         case LE_SS03_FEATURE_FLAG:
165             fFeatureMask |= ss03FeatureMask;
166             break;
167         case LE_SS04_FEATURE_FLAG:
168             fFeatureMask |= ss04FeatureMask;
169             break;
170         case LE_SS05_FEATURE_FLAG:
171             fFeatureMask |= ss05FeatureMask;
172             break;
173         case LE_SS06_FEATURE_FLAG:
174             fFeatureMask |= ss06FeatureMask;
175             break;
176         case LE_SS07_FEATURE_FLAG:
177             fFeatureMask |= ss07FeatureMask;
178             break;
179     }
180 
181     if (typoFlags & LE_Kerning_FEATURE_FLAG) {
182       fFeatureMask |= (kernFeatureMask | paltFeatureMask);
183       // Convenience.
184     }
185     if (typoFlags & LE_Ligatures_FEATURE_FLAG) {
186       fFeatureMask |= (ligaFeatureMask | cligFeatureMask);
187       // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ?
188     }
189     if (typoFlags & LE_CLIG_FEATURE_FLAG) fFeatureMask |= cligFeatureMask;
190     if (typoFlags & LE_DLIG_FEATURE_FLAG) fFeatureMask |= dligFeatureMask;
191     if (typoFlags & LE_HLIG_FEATURE_FLAG) fFeatureMask |= hligFeatureMask;
192     if (typoFlags & LE_LIGA_FEATURE_FLAG) fFeatureMask |= ligaFeatureMask;
193     if (typoFlags & LE_RLIG_FEATURE_FLAG) fFeatureMask |= rligFeatureMask;
194     if (typoFlags & LE_SMCP_FEATURE_FLAG) fFeatureMask |= smcpFeatureMask;
195     if (typoFlags & LE_FRAC_FEATURE_FLAG) fFeatureMask |= fracFeatureMask;
196     if (typoFlags & LE_AFRC_FEATURE_FLAG) fFeatureMask |= afrcFeatureMask;
197     if (typoFlags & LE_ZERO_FEATURE_FLAG) fFeatureMask |= zeroFeatureMask;
198     if (typoFlags & LE_SWSH_FEATURE_FLAG) fFeatureMask |= swshFeatureMask;
199     if (typoFlags & LE_CSWH_FEATURE_FLAG) fFeatureMask |= cswhFeatureMask;
200     if (typoFlags & LE_SALT_FEATURE_FLAG) fFeatureMask |= saltFeatureMask;
201     if (typoFlags & LE_RUBY_FEATURE_FLAG) fFeatureMask |= rubyFeatureMask;
202     if (typoFlags & LE_NALT_FEATURE_FLAG) {
203       // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm
204       fFeatureMask = naltFeatureMask;
205     }
206 
207     if (typoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
208       // This isn't a font feature, but requests a Char Substitution Filter
209       fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
210     }
211 
212 }
213 
reset()214 void OpenTypeLayoutEngine::reset()
215 {
216     // NOTE: if we're called from
217     // the destructor, LayoutEngine;:reset()
218     // will have been called already by
219     // LayoutEngine::~LayoutEngine()
220     LayoutEngine::reset();
221 }
222 
OpenTypeLayoutEngine(const LEFontInstance * fontInstance,le_int32 scriptCode,le_int32 languageCode,le_int32 typoFlags,LEErrorCode & success)223 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode,
224                        le_int32 typoFlags, LEErrorCode &success)
225     : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE),
226       fGSUBTable(), fGDEFTable(), fGPOSTable(), fSubstitutionFilter(NULL)
227 {
228   applyTypoFlags();
229   setScriptAndLanguageTags();
230 }
231 
~OpenTypeLayoutEngine()232 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
233 {
234     if (fTypoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
235         delete fSubstitutionFilter;
236         fSubstitutionFilter = NULL;
237     }
238 
239     reset();
240 }
241 
getScriptTag(le_int32 scriptCode)242 LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
243 {
244     if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
245         return 0xFFFFFFFF;
246     }
247     return scriptTags[scriptCode];
248 }
249 
getV2ScriptTag(le_int32 scriptCode)250 LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode)
251 {
252 	switch (scriptCode) {
253 		case bengScriptCode :    return bng2ScriptTag;
254 		case devaScriptCode :    return dev2ScriptTag;
255 		case gujrScriptCode :    return gjr2ScriptTag;
256 		case guruScriptCode :    return gur2ScriptTag;
257 		case kndaScriptCode :    return knd2ScriptTag;
258 		case mlymScriptCode :    return mlm2ScriptTag;
259 		case oryaScriptCode :    return ory2ScriptTag;
260 		case tamlScriptCode :    return tml2ScriptTag;
261 		case teluScriptCode :    return tel2ScriptTag;
262 		default:                 return nullScriptTag;
263 	}
264 }
265 
getLangSysTag(le_int32 languageCode)266 LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
267 {
268     if (languageCode < 0 || languageCode >= languageCodeCount) {
269         return 0xFFFFFFFF;
270     }
271 
272     return languageTags[languageCode];
273 }
274 
setScriptAndLanguageTags()275 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
276 {
277     fScriptTag  = getScriptTag(fScriptCode);
278     fScriptTagV2 = getV2ScriptTag(fScriptCode);
279     fLangSysTag = getLangSysTag(fLanguageCode);
280 }
281 
characterProcessing(const LEUnicode chars[],le_int32 offset,le_int32 count,le_int32 max,le_bool rightToLeft,LEUnicode * & outChars,LEGlyphStorage & glyphStorage,LEErrorCode & success)282 le_int32 OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
283                 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success)
284 {
285     if (LE_FAILURE(success)) {
286         return 0;
287     }
288 
289     if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
290         success = LE_ILLEGAL_ARGUMENT_ERROR;
291         return 0;
292     }
293 
294     // This is the cheapest way to get mark reordering only for Hebrew.
295     // We could just do the mark reordering for all scripts, but most
296     // of them probably don't need it... Another option would be to
297     // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
298     // would need to do is mark reordering, so that seems like overkill.
299     if (fScriptCode == hebrScriptCode) {
300         outChars = LE_NEW_ARRAY(LEUnicode, count);
301 
302         if (outChars == NULL) {
303             success = LE_MEMORY_ALLOCATION_ERROR;
304             return 0;
305         }
306 
307         if (LE_FAILURE(success)) {
308             LE_DELETE_ARRAY(outChars);
309             return 0;
310         }
311 
312         CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage);
313     }
314 
315     if (LE_FAILURE(success)) {
316         return 0;
317     }
318 
319     glyphStorage.allocateGlyphArray(count, rightToLeft, success);
320     glyphStorage.allocateAuxData(success);
321 
322     for (le_int32 i = 0; i < count; i += 1) {
323         glyphStorage.setAuxData(i, fFeatureMask, success);
324     }
325 
326     return count;
327 }
328 
329 // Input: characters, tags
330 // Output: glyphs, char indices
glyphProcessing(const LEUnicode chars[],le_int32 offset,le_int32 count,le_int32 max,le_bool rightToLeft,LEGlyphStorage & glyphStorage,LEErrorCode & success)331 le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
332                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
333 {
334     if (LE_FAILURE(success)) {
335         return 0;
336     }
337 
338     if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
339         success = LE_ILLEGAL_ARGUMENT_ERROR;
340         return 0;
341     }
342 
343     mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
344 
345     if (LE_FAILURE(success)) {
346         return 0;
347     }
348 
349     if (fGSUBTable.isValid()) {
350       if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable, fScriptTagV2, fLangSysTag, success)) {
351           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
352                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
353 
354         } else {
355           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
356                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
357         }
358     }
359 
360     return count;
361 }
362 // Input: characters, tags
363 // Output: glyphs, char indices
glyphSubstitution(le_int32 count,le_int32 max,le_bool rightToLeft,LEGlyphStorage & glyphStorage,LEErrorCode & success)364 le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft,
365                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
366 {
367     if (LE_FAILURE(success)) {
368         return 0;
369     }
370 
371     if ( count < 0 || max < 0 ) {
372         success = LE_ILLEGAL_ARGUMENT_ERROR;
373         return 0;
374     }
375 
376     if (fGSUBTable.isValid()) {
377        if (fScriptTagV2 != nullScriptTag && fGSUBTable->coversScriptAndLanguage(fGSUBTable,fScriptTagV2,fLangSysTag,success)) {
378           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTagV2, fLangSysTag, fGDEFTable, fSubstitutionFilter,
379                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
380 
381         } else {
382           count = fGSUBTable->process(fGSUBTable, glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
383                                     fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
384         }
385     }
386 
387     return count;
388 }
glyphPostProcessing(LEGlyphStorage & tempGlyphStorage,LEGlyphStorage & glyphStorage,LEErrorCode & success)389 le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success)
390 {
391     if (LE_FAILURE(success)) {
392         return 0;
393     }
394 
395     glyphStorage.adoptGlyphArray(tempGlyphStorage);
396     glyphStorage.adoptCharIndicesArray(tempGlyphStorage);
397     glyphStorage.adoptAuxDataArray(tempGlyphStorage);
398     glyphStorage.adoptGlyphCount(tempGlyphStorage);
399 
400     return glyphStorage.getGlyphCount();
401 }
402 
computeGlyphs(const LEUnicode chars[],le_int32 offset,le_int32 count,le_int32 max,le_bool rightToLeft,LEGlyphStorage & glyphStorage,LEErrorCode & success)403 le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success)
404 {
405     LEUnicode *outChars = NULL;
406     LEGlyphStorage fakeGlyphStorage;
407     le_int32 outCharCount, outGlyphCount;
408 
409     if (LE_FAILURE(success)) {
410         return 0;
411     }
412 
413     if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
414         success = LE_ILLEGAL_ARGUMENT_ERROR;
415         return 0;
416     }
417 
418     outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success);
419 
420     if (LE_FAILURE(success)) {
421         return 0;
422     }
423 
424     if (outChars != NULL) {
425         // le_int32 fakeGlyphCount =
426         glyphProcessing(outChars, 0, outCharCount, outCharCount, rightToLeft, fakeGlyphStorage, success);
427         LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
428         //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
429     } else {
430         // le_int32 fakeGlyphCount =
431         glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
432         //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
433     }
434 
435     if (LE_FAILURE(success)) {
436         return 0;
437     }
438 
439     outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
440 
441     return outGlyphCount;
442 }
443 
444 // apply GPOS table, if any
adjustGlyphPositions(const LEUnicode chars[],le_int32 offset,le_int32 count,le_bool reverse,LEGlyphStorage & glyphStorage,LEErrorCode & success)445 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
446                                                 LEGlyphStorage &glyphStorage, LEErrorCode &success)
447 {
448     if (LE_FAILURE(success)) {
449         return;
450     }
451 
452     if (chars == NULL || offset < 0 || count < 0) {
453         success = LE_ILLEGAL_ARGUMENT_ERROR;
454         return;
455     }
456 
457     le_int32 glyphCount = glyphStorage.getGlyphCount();
458     if (glyphCount == 0) {
459         return;
460     }
461 
462     if (!fGPOSTable.isEmpty()) {
463         GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
464         le_int32 i;
465 
466         if (adjustments == NULL) {
467             success = LE_MEMORY_ALLOCATION_ERROR;
468             return;
469         }
470 
471 #if 0
472         // Don't need to do this if we allocate
473         // the adjustments array w/ new...
474         for (i = 0; i < glyphCount; i += 1) {
475             adjustments->setXPlacement(i, 0);
476             adjustments->setYPlacement(i, 0);
477 
478             adjustments->setXAdvance(i, 0);
479             adjustments->setYAdvance(i, 0);
480 
481             adjustments->setBaseOffset(i, -1);
482         }
483 #endif
484 
485         if (!fGPOSTable.isEmpty()) {
486             if (fScriptTagV2 != nullScriptTag &&
487                 fGPOSTable->coversScriptAndLanguage(fGPOSTable, fScriptTagV2,fLangSysTag,success)) {
488               fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTagV2, fLangSysTag,
489                                   fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
490 
491             } else {
492               fGPOSTable->process(fGPOSTable, glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag,
493                                   fGDEFTable, success, fFontInstance, fFeatureMap, fFeatureMapCount, fFeatureOrder);
494             }
495         } else if (fTypoFlags & LE_Kerning_FEATURE_FLAG) { /* kerning enabled */
496           LETableReference kernTable(fFontInstance, LE_KERN_TABLE_TAG, success);
497           KernTable kt(kernTable, success);
498           kt.process(glyphStorage, success);
499         }
500 
501         float xAdjust = 0, yAdjust = 0;
502 
503         for (i = 0; i < glyphCount; i += 1) {
504             float xAdvance   = adjustments->getXAdvance(i);
505             float yAdvance   = adjustments->getYAdvance(i);
506             float xPlacement = 0;
507             float yPlacement = 0;
508 
509 
510 #if 0
511             // This is where separate kerning adjustments
512             // should get applied.
513             xAdjust += xKerning;
514             yAdjust += yKerning;
515 #endif
516 
517             for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
518                 xPlacement += adjustments->getXPlacement(base);
519                 yPlacement += adjustments->getYPlacement(base);
520             }
521 
522             xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
523             yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
524             glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
525 
526             xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
527             yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
528         }
529 
530         glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
531 
532         delete adjustments;
533     } else {
534         // if there was no GPOS table, maybe there's non-OpenType kerning we can use
535         LayoutEngine::adjustGlyphPositions(chars, offset, count, reverse, glyphStorage, success);
536     }
537 
538     LEGlyphID zwnj  = fFontInstance->mapCharToGlyph(0x200C);
539 
540     if (zwnj != 0x0000) {
541         for (le_int32 g = 0; g < glyphCount; g += 1) {
542             LEGlyphID glyph = glyphStorage[g];
543 
544             if (glyph == zwnj) {
545                 glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
546             }
547         }
548     }
549 
550 #if 0
551     // Don't know why this is here...
552     LE_DELETE_ARRAY(fFeatureTags);
553     fFeatureTags = NULL;
554 #endif
555 }
556 
557 U_NAMESPACE_END
558