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