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
54U_NAMESPACE_BEGIN
55
56UOBJECT_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
121static 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
151static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
152
153OpenTypeLayoutEngine::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
172void 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
239void 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
248OpenTypeLayoutEngine::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
257OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
258{
259    if (fTypoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
260        delete fSubstitutionFilter;
261        fSubstitutionFilter = NULL;
262    }
263
264    reset();
265}
266
267LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
268{
269    if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
270        return 0xFFFFFFFF;
271    }
272    return scriptTags[scriptCode];
273}
274
275LETag 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
291LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
292{
293    if (languageCode < 0 || languageCode >= languageCodeCount) {
294        return 0xFFFFFFFF;
295    }
296
297    return languageTags[languageCode];
298}
299
300void OpenTypeLayoutEngine::setScriptAndLanguageTags()
301{
302    fScriptTag  = getScriptTag(fScriptCode);
303    fScriptTagV2 = getV2ScriptTag(fScriptCode);
304    fLangSysTag = getLangSysTag(fLanguageCode);
305}
306
307le_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
356le_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
389le_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}
414le_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
428le_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
470void 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 = 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
587U_NAMESPACE_END
588