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