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