1/*
2 * (C) Copyright IBM Corp. and others 1998-2014 - All Rights Reserved
3 */
4
5#include "LETypes.h"
6#include "LEScripts.h"
7#include "LELanguages.h"
8#include "LESwaps.h"
9
10#include "LayoutEngine.h"
11#include "ArabicLayoutEngine.h"
12#include "CanonShaping.h"
13#include "HanLayoutEngine.h"
14#include "HangulLayoutEngine.h"
15#include "IndicLayoutEngine.h"
16#include "KhmerLayoutEngine.h"
17#include "ThaiLayoutEngine.h"
18#include "TibetanLayoutEngine.h"
19#include "GXLayoutEngine.h"
20#include "GXLayoutEngine2.h"
21
22#include "ScriptAndLanguageTags.h"
23#include "CharSubstitutionFilter.h"
24
25#include "LEGlyphStorage.h"
26
27#include "OpenTypeUtilities.h"
28#include "GlyphSubstitutionTables.h"
29#include "GlyphDefinitionTables.h"
30#include "MorphTables.h"
31
32#include "DefaultCharMapper.h"
33
34#include "KernTable.h"
35
36U_NAMESPACE_BEGIN
37
38/* Leave this copyright notice here! It needs to go somewhere in this library. */
39static const char copyright[] = U_COPYRIGHT_STRING;
40
41/* TODO: remove these? */
42const le_int32 LayoutEngine::kTypoFlagKern = LE_Kerning_FEATURE_FLAG;
43const le_int32 LayoutEngine::kTypoFlagLiga = LE_Ligatures_FEATURE_FLAG;
44
45const LEUnicode32 DefaultCharMapper::controlChars[] = {
46    0x0009, 0x000A, 0x000D,
47    /*0x200C, 0x200D,*/ 0x200E, 0x200F,
48    0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E,
49    0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F
50};
51
52const le_int32 DefaultCharMapper::controlCharsCount = LE_ARRAY_SIZE(controlChars);
53
54LEUnicode32 DefaultCharMapper::mapChar(LEUnicode32 ch) const
55{
56    (void)copyright;    // Suppress unused variable warning.
57    if (fFilterControls) {
58        le_int32 index = OpenTypeUtilities::search((le_uint32)ch, (le_uint32 *)controlChars, controlCharsCount);
59
60        if (controlChars[index] == ch) {
61            return 0xFFFF;
62        }
63    }
64
65    if (fMirror) {
66        le_int32 index = OpenTypeUtilities::search((le_uint32) ch, (le_uint32 *)DefaultCharMapper::mirroredChars, DefaultCharMapper::mirroredCharsCount);
67
68        if (mirroredChars[index] == ch) {
69            return DefaultCharMapper::srahCderorrim[index];
70        }
71    }
72
73    return ch;
74}
75
76// This is here to get it out of LEGlyphFilter.h.
77// No particular reason to put it here, other than
78// this is a good central location...
79LEGlyphFilter::~LEGlyphFilter()
80{
81    // nothing to do
82}
83
84CharSubstitutionFilter::CharSubstitutionFilter(const LEFontInstance *fontInstance)
85  : fFontInstance(fontInstance)
86{
87    // nothing to do
88}
89
90CharSubstitutionFilter::~CharSubstitutionFilter()
91{
92    // nothing to do
93}
94
95class CanonMarkFilter : public UMemory, public LEGlyphFilter
96{
97private:
98  const LEReferenceTo<GlyphClassDefinitionTable> classDefTable;
99
100    CanonMarkFilter(const CanonMarkFilter &other); // forbid copying of this class
101    CanonMarkFilter &operator=(const CanonMarkFilter &other); // forbid copying of this class
102
103public:
104    CanonMarkFilter(const LEReferenceTo<GlyphDefinitionTableHeader> &gdefTable, LEErrorCode &success);
105    virtual ~CanonMarkFilter();
106
107    virtual le_bool accept(LEGlyphID glyph) const;
108};
109
110CanonMarkFilter::CanonMarkFilter(const LEReferenceTo<GlyphDefinitionTableHeader> &gdefTable, LEErrorCode &success)
111  : classDefTable(gdefTable->getMarkAttachClassDefinitionTable(gdefTable, success))
112{
113}
114
115CanonMarkFilter::~CanonMarkFilter()
116{
117    // nothing to do?
118}
119
120le_bool CanonMarkFilter::accept(LEGlyphID glyph) const
121{
122  LEErrorCode success = LE_NO_ERROR;
123  le_int32 glyphClass = classDefTable->getGlyphClass(classDefTable, glyph, success);
124  if(LE_FAILURE(success)) return false;
125  return glyphClass != 0;
126}
127
128UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LayoutEngine)
129
130#define ccmpFeatureTag  LE_CCMP_FEATURE_TAG
131
132#define ccmpFeatureMask 0x80000000UL
133
134#define canonFeatures (ccmpFeatureMask)
135
136static const FeatureMap canonFeatureMap[] =
137{
138    {ccmpFeatureTag, ccmpFeatureMask}
139};
140
141static const le_int32 canonFeatureMapCount = LE_ARRAY_SIZE(canonFeatureMap);
142
143LayoutEngine::LayoutEngine(const LEFontInstance *fontInstance,
144                           le_int32 scriptCode,
145                           le_int32 languageCode,
146                           le_int32 typoFlags,
147                           LEErrorCode &success)
148  : fGlyphStorage(NULL), fFontInstance(fontInstance), fScriptCode(scriptCode), fLanguageCode(languageCode),
149    fTypoFlags(typoFlags), fFilterZeroWidth(TRUE)
150{
151    if (LE_FAILURE(success)) {
152        return;
153    }
154
155    fGlyphStorage = new LEGlyphStorage();
156    if (fGlyphStorage == NULL) {
157        success = LE_MEMORY_ALLOCATION_ERROR;
158    }
159}
160
161le_int32 LayoutEngine::getGlyphCount() const
162{
163    return fGlyphStorage->getGlyphCount();
164}
165
166void LayoutEngine::getCharIndices(le_int32 charIndices[], le_int32 indexBase, LEErrorCode &success) const
167{
168    fGlyphStorage->getCharIndices(charIndices, indexBase, success);
169}
170
171void LayoutEngine::getCharIndices(le_int32 charIndices[], LEErrorCode &success) const
172{
173    fGlyphStorage->getCharIndices(charIndices, success);
174}
175
176// Copy the glyphs into caller's (32-bit) glyph array, OR in extraBits
177void LayoutEngine::getGlyphs(le_uint32 glyphs[], le_uint32 extraBits, LEErrorCode &success) const
178{
179    fGlyphStorage->getGlyphs(glyphs, extraBits, success);
180}
181
182void LayoutEngine::getGlyphs(LEGlyphID glyphs[], LEErrorCode &success) const
183{
184    fGlyphStorage->getGlyphs(glyphs, success);
185}
186
187
188void LayoutEngine::getGlyphPositions(float positions[], LEErrorCode &success) const
189{
190    fGlyphStorage->getGlyphPositions(positions, success);
191}
192
193void LayoutEngine::getGlyphPosition(le_int32 glyphIndex, float &x, float &y, LEErrorCode &success) const
194{
195    fGlyphStorage->getGlyphPosition(glyphIndex, x, y, success);
196}
197
198le_int32 LayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
199                LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success)
200{
201    if (LE_FAILURE(success)) {
202        return 0;
203    }
204
205    if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
206        success = LE_ILLEGAL_ARGUMENT_ERROR;
207        return 0;
208    }
209
210    LEReferenceTo<GlyphSubstitutionTableHeader> canonGSUBTable((GlyphSubstitutionTableHeader *) CanonShaping::glyphSubstitutionTable);
211    LETag scriptTag  = OpenTypeLayoutEngine::getScriptTag(fScriptCode);
212    LETag langSysTag = OpenTypeLayoutEngine::getLangSysTag(fLanguageCode);
213    le_int32 i, dir = 1, out = 0, outCharCount = count;
214
215    if (canonGSUBTable->coversScript(canonGSUBTable,scriptTag, success) || LE_SUCCESS(success)) {
216        CharSubstitutionFilter *substitutionFilter = new CharSubstitutionFilter(fFontInstance);
217        if (substitutionFilter == NULL) {
218            success = LE_MEMORY_ALLOCATION_ERROR;
219            return 0;
220        }
221
222        const LEUnicode *inChars = &chars[offset];
223        LEUnicode *reordered = NULL;
224        LEGlyphStorage fakeGlyphStorage;
225
226        fakeGlyphStorage.allocateGlyphArray(count, rightToLeft, success);
227
228        if (LE_FAILURE(success)) {
229            delete substitutionFilter;
230            return 0;
231        }
232
233        // This is the cheapest way to get mark reordering only for Hebrew.
234        // We could just do the mark reordering for all scripts, but most
235        // of them probably don't need it...
236        if (fScriptCode == hebrScriptCode) {
237          reordered = LE_NEW_ARRAY(LEUnicode, count);
238
239          if (reordered == NULL) {
240            delete substitutionFilter;
241            success = LE_MEMORY_ALLOCATION_ERROR;
242            return 0;
243          }
244
245          CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, reordered, fakeGlyphStorage);
246          inChars = reordered;
247        }
248
249        fakeGlyphStorage.allocateAuxData(success);
250
251        if (LE_FAILURE(success)) {
252            delete substitutionFilter;
253            return 0;
254        }
255
256        if (rightToLeft) {
257            out = count - 1;
258            dir = -1;
259        }
260
261        for (i = 0; i < count; i += 1, out += dir) {
262            fakeGlyphStorage[out] = (LEGlyphID) inChars[i];
263            fakeGlyphStorage.setAuxData(out, canonFeatures, success);
264        }
265
266        if (reordered != NULL) {
267          LE_DELETE_ARRAY(reordered);
268        }
269
270        outCharCount = canonGSUBTable->process(canonGSUBTable, fakeGlyphStorage, rightToLeft, scriptTag, langSysTag, (const GlyphDefinitionTableHeader*)NULL, substitutionFilter, canonFeatureMap, canonFeatureMapCount, FALSE, success);
271
272        if (LE_FAILURE(success)) {
273            delete substitutionFilter;
274            return 0;
275        }
276
277        out = (rightToLeft? outCharCount - 1 : 0);
278
279        /*
280         * The char indices array in fakeGlyphStorage has the correct mapping
281         * back to the original input characters. Save it in glyphStorage. The
282         * subsequent call to glyphStoratge.allocateGlyphArray will keep this
283         * array rather than allocating and initializing a new one.
284         */
285        glyphStorage.adoptCharIndicesArray(fakeGlyphStorage);
286
287        outChars = LE_NEW_ARRAY(LEUnicode, outCharCount);
288
289        if (outChars == NULL) {
290            delete substitutionFilter;
291            success = LE_MEMORY_ALLOCATION_ERROR;
292            return 0;
293        }
294
295        for (i = 0; i < outCharCount; i += 1, out += dir) {
296            outChars[out] = (LEUnicode) LE_GET_GLYPH(fakeGlyphStorage[i]);
297        }
298
299        delete substitutionFilter;
300    }
301
302    return outCharCount;
303}
304
305le_int32 LayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
306                                            LEGlyphStorage &glyphStorage, LEErrorCode &success)
307{
308    if (LE_FAILURE(success)) {
309        return 0;
310    }
311
312    if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
313        success = LE_ILLEGAL_ARGUMENT_ERROR;
314        return 0;
315    }
316
317    LEUnicode *outChars = NULL;
318    le_int32 outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, glyphStorage, success);
319
320    if (outChars != NULL) {
321        mapCharsToGlyphs(outChars, 0, outCharCount, rightToLeft, rightToLeft, glyphStorage, success);
322        LE_DELETE_ARRAY(outChars); // FIXME: a subclass may have allocated this, in which case this delete might not work...
323    } else {
324        mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
325    }
326
327    return glyphStorage.getGlyphCount();
328}
329
330// Input: glyphs
331// Output: positions
332void LayoutEngine::positionGlyphs(LEGlyphStorage &glyphStorage, float x, float y, LEErrorCode &success)
333{
334    if (LE_FAILURE(success)) {
335        return;
336    }
337
338    glyphStorage.allocatePositions(success);
339
340    if (LE_FAILURE(success)) {
341        return;
342    }
343
344    le_int32 i, glyphCount = glyphStorage.getGlyphCount();
345
346    for (i = 0; i < glyphCount; i += 1) {
347        LEPoint advance;
348
349        glyphStorage.setPosition(i, x, y, success);
350
351        fFontInstance->getGlyphAdvance(glyphStorage[i], advance);
352        x += advance.fX;
353        y += advance.fY;
354    }
355
356    glyphStorage.setPosition(glyphCount, x, y, success);
357}
358
359void LayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool  reverse,
360                                        LEGlyphStorage &glyphStorage, LEErrorCode &success)
361{
362    if (LE_FAILURE(success)) {
363        return;
364    }
365
366    if (chars == NULL || offset < 0 || count < 0) {
367        success = LE_ILLEGAL_ARGUMENT_ERROR;
368        return;
369    }
370
371    LEReferenceTo<GlyphDefinitionTableHeader> gdefTable((GlyphDefinitionTableHeader *) CanonShaping::glyphDefinitionTable,
372                                                        CanonShaping::glyphDefinitionTableLen);
373    CanonMarkFilter filter(gdefTable, success);
374
375    adjustMarkGlyphs(&chars[offset], count, reverse, glyphStorage, &filter, success);
376
377    if (fTypoFlags & LE_Kerning_FEATURE_FLAG) { /* kerning enabled */
378      LETableReference kernTable(fFontInstance, LE_KERN_TABLE_TAG, success);
379      KernTable kt(kernTable, success);
380      kt.process(glyphStorage, success);
381    }
382
383    // default is no adjustments
384    return;
385}
386
387void LayoutEngine::adjustMarkGlyphs(LEGlyphStorage &glyphStorage, LEGlyphFilter *markFilter, LEErrorCode &success)
388{
389    float xAdjust = 0;
390    le_int32 p, glyphCount = glyphStorage.getGlyphCount();
391
392    if (LE_FAILURE(success)) {
393        return;
394    }
395
396    if (markFilter == NULL) {
397        success = LE_ILLEGAL_ARGUMENT_ERROR;
398        return;
399    }
400
401    float ignore, prev;
402
403    glyphStorage.getGlyphPosition(0, prev, ignore, success);
404
405    for (p = 0; p < glyphCount; p += 1) {
406        float next, xAdvance;
407
408        glyphStorage.getGlyphPosition(p + 1, next, ignore, success);
409
410        xAdvance = next - prev;
411        glyphStorage.adjustPosition(p, xAdjust, 0, success);
412
413        if (markFilter->accept(glyphStorage[p])) {
414            xAdjust -= xAdvance;
415        }
416
417        prev = next;
418    }
419
420    glyphStorage.adjustPosition(glyphCount, xAdjust, 0, success);
421}
422
423void LayoutEngine::adjustMarkGlyphs(const LEUnicode chars[], le_int32 charCount, le_bool reverse, LEGlyphStorage &glyphStorage, LEGlyphFilter *markFilter, LEErrorCode &success)
424{
425    float xAdjust = 0;
426    le_int32 c = 0, direction = 1, p;
427    le_int32 glyphCount = glyphStorage.getGlyphCount();
428
429    if (LE_FAILURE(success)) {
430        return;
431    }
432
433    if (markFilter == NULL) {
434        success = LE_ILLEGAL_ARGUMENT_ERROR;
435        return;
436    }
437
438    if (reverse) {
439        c = glyphCount - 1;
440        direction = -1;
441    }
442
443    float ignore, prev;
444
445    glyphStorage.getGlyphPosition(0, prev, ignore, success);
446
447    for (p = 0; p < charCount; p += 1, c += direction) {
448        float next, xAdvance;
449
450        glyphStorage.getGlyphPosition(p + 1, next, ignore, success);
451
452        xAdvance = next - prev;
453        glyphStorage.adjustPosition(p, xAdjust, 0, success);
454
455        if (markFilter->accept(chars[c])) {
456            xAdjust -= xAdvance;
457        }
458
459        prev = next;
460    }
461
462    glyphStorage.adjustPosition(glyphCount, xAdjust, 0, success);
463}
464
465const void *LayoutEngine::getFontTable(LETag tableTag, size_t &length) const
466{
467  return fFontInstance->getFontTable(tableTag, length);
468}
469
470void LayoutEngine::mapCharsToGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, le_bool mirror,
471                                    LEGlyphStorage &glyphStorage, LEErrorCode &success)
472{
473    if (LE_FAILURE(success)) {
474        return;
475    }
476
477    glyphStorage.allocateGlyphArray(count, reverse, success);
478
479    DefaultCharMapper charMapper(TRUE, mirror);
480
481    fFontInstance->mapCharsToGlyphs(chars, offset, count, reverse, &charMapper, fFilterZeroWidth, glyphStorage);
482}
483
484// Input: characters, font?
485// Output: glyphs, positions, char indices
486// Returns: number of glyphs
487le_int32 LayoutEngine::layoutChars(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
488                              float x, float y, LEErrorCode &success)
489{
490    if (LE_FAILURE(success)) {
491        return 0;
492    }
493
494    if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
495        success = LE_ILLEGAL_ARGUMENT_ERROR;
496        return 0;
497    }
498
499    le_int32 glyphCount;
500
501    if (fGlyphStorage->getGlyphCount() > 0) {
502        fGlyphStorage->reset();
503    }
504
505    glyphCount = computeGlyphs(chars, offset, count, max, rightToLeft, *fGlyphStorage, success);
506    positionGlyphs(*fGlyphStorage, x, y, success);
507    adjustGlyphPositions(chars, offset, count, rightToLeft, *fGlyphStorage, success);
508
509    return glyphCount;
510}
511
512void LayoutEngine::reset()
513{
514  if(fGlyphStorage!=NULL) {
515    fGlyphStorage->reset();
516  }
517}
518
519LayoutEngine *LayoutEngine::layoutEngineFactory(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, LEErrorCode &success)
520{
521  //kerning and ligatures - by default
522  return LayoutEngine::layoutEngineFactory(fontInstance, scriptCode, languageCode, LE_DEFAULT_FEATURE_FLAG, success);
523}
524
525LayoutEngine *LayoutEngine::layoutEngineFactory(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, le_int32 typoFlags, LEErrorCode &success)
526{
527    static const le_uint32 gsubTableTag = LE_GSUB_TABLE_TAG;
528    static const le_uint32 mortTableTag = LE_MORT_TABLE_TAG;
529    static const le_uint32 morxTableTag = LE_MORX_TABLE_TAG;
530
531    if (LE_FAILURE(success)) {
532        return NULL;
533    }
534
535    LEReferenceTo<GlyphSubstitutionTableHeader> gsubTable(fontInstance,gsubTableTag,success);
536    LayoutEngine *result = NULL;
537    LETag scriptTag   = 0x00000000;
538    LETag languageTag = 0x00000000;
539    LETag v2ScriptTag = OpenTypeLayoutEngine::getV2ScriptTag(scriptCode);
540
541    // Right now, only invoke V2 processing for Devanagari.  TODO: Allow more V2 scripts as they are
542    // properly tested.
543
544    if ( v2ScriptTag == dev2ScriptTag && gsubTable.isValid() && gsubTable->coversScript(gsubTable, v2ScriptTag, success )) {
545      result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, TRUE, gsubTable, success);
546    }
547    else if (gsubTable.isValid() && gsubTable->coversScript(gsubTable, scriptTag = OpenTypeLayoutEngine::getScriptTag(scriptCode), success)) {
548        switch (scriptCode) {
549        case bengScriptCode:
550        case devaScriptCode:
551        case gujrScriptCode:
552        case kndaScriptCode:
553        case mlymScriptCode:
554        case oryaScriptCode:
555        case guruScriptCode:
556        case tamlScriptCode:
557        case teluScriptCode:
558        case sinhScriptCode:
559            result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, FALSE, gsubTable, success);
560            break;
561
562        case arabScriptCode:
563            result = new ArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success);
564            break;
565
566        case hebrScriptCode:
567            // Disable hebrew ligatures since they have only archaic uses, see ticket #8318
568            result = new OpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags & ~kTypoFlagLiga, gsubTable, success);
569            break;
570
571        case hangScriptCode:
572            result = new HangulOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success);
573            break;
574
575        case haniScriptCode:
576            languageTag = OpenTypeLayoutEngine::getLangSysTag(languageCode);
577
578            switch (languageCode) {
579            case korLanguageCode:
580            case janLanguageCode:
581            case zhtLanguageCode:
582            case zhsLanguageCode:
583              if (gsubTable->coversScriptAndLanguage(gsubTable, scriptTag, languageTag, success, TRUE)) {
584                    result = new HanOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success);
585                    break;
586              }
587
588                // note: falling through to default case.
589            default:
590                result = new OpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success);
591                break;
592            }
593
594            break;
595
596        case tibtScriptCode:
597            result = new TibetanOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success);
598            break;
599
600        case khmrScriptCode:
601            result = new KhmerOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success);
602            break;
603
604        default:
605            result = new OpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, gsubTable, success);
606            break;
607        }
608    } else {
609        MorphTableHeader2 *morxTable = (MorphTableHeader2 *)fontInstance->getFontTable(morxTableTag);
610        if (morxTable != NULL && SWAPL(morxTable->version)==0x00020000) {
611            result = new GXLayoutEngine2(fontInstance, scriptCode, languageCode, morxTable, typoFlags, success);
612        } else {
613          LEReferenceTo<MorphTableHeader> mortTable(fontInstance, mortTableTag, success);
614          if (LE_SUCCESS(success) && mortTable.isValid() && SWAPL(mortTable->version)==0x00010000) { // mort
615            result = new GXLayoutEngine(fontInstance, scriptCode, languageCode, mortTable, success);
616            } else {
617                switch (scriptCode) {
618                    case bengScriptCode:
619                    case devaScriptCode:
620                    case gujrScriptCode:
621                    case kndaScriptCode:
622                    case mlymScriptCode:
623                    case oryaScriptCode:
624                    case guruScriptCode:
625                    case tamlScriptCode:
626                    case teluScriptCode:
627                    case sinhScriptCode:
628                    {
629                        result = new IndicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success);
630                        break;
631                    }
632
633                    case arabScriptCode:
634                        //case hebrScriptCode:
635                        result = new UnicodeArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success);
636                        break;
637
638                        //case hebrScriptCode:
639                        //    return new HebrewOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags);
640
641                    case thaiScriptCode:
642                        result = new ThaiLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success);
643                        break;
644
645                    case hangScriptCode:
646                        result = new HangulOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success);
647                        break;
648
649                    default:
650                        result = new LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success);
651                        break;
652                }
653            }
654        }
655    }
656
657    if (result && LE_FAILURE(success)) {
658		delete result;
659		result = NULL;
660	}
661
662    if (result == NULL) {
663        success = LE_MEMORY_ALLOCATION_ERROR;
664    }
665
666    return result;
667}
668
669LayoutEngine::~LayoutEngine() {
670    delete fGlyphStorage;
671}
672
673U_NAMESPACE_END
674