91d89b88c380b51577e6dcc844e63003ccbb7424
[platform/upstream/icu.git] / source / layoutex / ParagraphLayout.cpp
1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4  **********************************************************************
5  *   Copyright (C) 2002-2014, International Business Machines
6  *   Corporation and others.  All Rights Reserved.
7  **********************************************************************
8  */
9
10 /*
11  * paragraphLayout doesn't make much sense without
12  * BreakIterator...
13  */
14 #include "layout/LETypes.h"
15 #include "layout/LEScripts.h"
16 #include "layout/LELanguages.h"
17 #include "layout/LayoutEngine.h"
18 #include "layout/LEFontInstance.h"
19
20 #include "unicode/ubidi.h"
21 #include "unicode/uchriter.h"
22 #include "unicode/brkiter.h"
23
24 #if ! UCONFIG_NO_BREAK_ITERATION
25 #include "LXUtilities.h"
26 #include "usc_impl.h" /* this is currently private! */
27 #include "cstring.h"  /* this too! */
28
29 #include "layout/ParagraphLayout.h"
30
31 U_NAMESPACE_BEGIN
32
33 #define ARRAY_SIZE(array) (sizeof array  / sizeof array[0])
34
35 /* Leave this copyright notice here! It needs to go somewhere in this library. */
36 static const char copyright[] = U_COPYRIGHT_STRING;
37
38 class StyleRuns
39 {
40 public:
41     StyleRuns(const RunArray *styleRunArrays[], le_int32 styleCount);
42
43     ~StyleRuns();
44
45     le_int32 getRuns(le_int32 runLimits[], le_int32 styleIndices[]);
46
47 private:
48     le_int32 fStyleCount;
49     le_int32 fRunCount;
50
51     le_int32 *fRunLimits;
52     le_int32 *fStyleIndices;
53 };
54
55 StyleRuns::StyleRuns(const RunArray *styleRunArrays[], le_int32 styleCount)
56     : fStyleCount(styleCount), fRunCount(0), fRunLimits(NULL), fStyleIndices(NULL)
57 {
58     le_int32 maxRunCount = 0;
59     le_int32 style, run, runStyle;
60     le_int32 *currentRun = LE_NEW_ARRAY(le_int32, styleCount);
61
62     for (int i = 0; i < styleCount; i += 1) {
63         maxRunCount += styleRunArrays[i]->getCount();
64     }
65
66     maxRunCount -= styleCount - 1;
67
68     fRunLimits    = LE_NEW_ARRAY(le_int32, maxRunCount);
69     fStyleIndices = LE_NEW_ARRAY(le_int32, maxRunCount * styleCount);
70
71     for (style = 0; style < styleCount; style += 1) {
72         currentRun[style] = 0;
73     }
74
75     run = 0;
76     runStyle = 0;
77
78     /*
79      * Since the last run limit for each style run must be
80      * the same, all the styles will hit the last limit at
81      * the same time, so we know when we're done when the first
82      * style hits the last limit.
83      */
84     while (currentRun[0] < styleRunArrays[0]->getCount()) {
85         fRunLimits[run] = 0x7FFFFFFF;
86
87         // find the minimum run limit for all the styles
88         for (style = 0; style < styleCount; style += 1) {
89             if (styleRunArrays[style]->getLimit(currentRun[style]) < fRunLimits[run]) {
90                 fRunLimits[run] = styleRunArrays[style]->getLimit(currentRun[style]);
91             }
92         }
93
94         // advance all styles whose current run is at this limit to the next run
95         for (style = 0; style < styleCount; style += 1) {
96             fStyleIndices[runStyle++] = currentRun[style];
97
98             if (styleRunArrays[style]->getLimit(currentRun[style]) == fRunLimits[run]) {
99                 currentRun[style] += 1;
100             }
101         }
102
103         run += 1;
104     }
105
106     fRunCount = run;
107     LE_DELETE_ARRAY(currentRun);
108 }
109
110 StyleRuns::~StyleRuns()
111 {
112     fRunCount = 0;
113
114     LE_DELETE_ARRAY(fStyleIndices);
115     fStyleIndices = NULL;
116
117     LE_DELETE_ARRAY(fRunLimits);
118     fRunLimits = NULL;
119 }
120
121 le_int32 StyleRuns::getRuns(le_int32 runLimits[], le_int32 styleIndices[])
122 {
123     if (runLimits != NULL) {
124         LE_ARRAY_COPY(runLimits, fRunLimits, fRunCount);
125     }
126
127     if (styleIndices != NULL) {
128         LE_ARRAY_COPY(styleIndices, fStyleIndices, fRunCount * fStyleCount);
129     }
130
131     return fRunCount;
132 }
133
134 /*
135  * NOTE: This table only has "TRUE" values for
136  * those scripts which the LayoutEngine can currently
137  * process, rather for all scripts which require
138  * complex processing for correct rendering.
139  */
140 static const le_bool complexTable[scriptCodeCount] = {
141     FALSE , /* Zyyy */
142     FALSE,  /* Qaai */
143     TRUE,   /* Arab */
144     FALSE,  /* Armn */
145     TRUE,   /* Beng */
146     FALSE,  /* Bopo */
147     FALSE,  /* Cher */
148     FALSE,  /* Copt=Qaac */
149     FALSE,  /* Cyrl */
150     FALSE,  /* Dsrt */
151     TRUE,   /* Deva */
152     FALSE,  /* Ethi */
153     FALSE,  /* Geor */
154     FALSE,  /* Goth */
155     FALSE,  /* Grek */
156     TRUE,   /* Gujr */
157     TRUE,   /* Guru */
158     FALSE,  /* Hani */
159     FALSE,  /* Hang */
160     TRUE,   /* Hebr */
161     FALSE,  /* Hira */
162     TRUE,   /* Knda */
163     FALSE,  /* Kana */
164     FALSE,  /* Khmr */
165     FALSE,  /* Laoo */
166     FALSE,  /* Latn */
167     TRUE,   /* Mlym */
168     FALSE,  /* Mong */
169     FALSE,  /* Mymr */
170     FALSE,  /* Ogam */
171     FALSE,  /* Ital */
172     TRUE,   /* Orya */
173     FALSE,  /* Runr */
174     FALSE,  /* Sinh */
175     FALSE,  /* Syrc */
176     TRUE,   /* Taml */
177     TRUE,   /* Telu */
178     FALSE,  /* Thaa */
179     TRUE,   /* Thai */
180     FALSE,  /* Tibt */
181     FALSE,  /* Cans */
182     FALSE,  /* Yiii */
183     FALSE,  /* Tglg */
184     FALSE,  /* Hano */
185     FALSE,  /* Buhd */
186     FALSE,  /* Tagb */
187     FALSE,  /* Brai */
188     FALSE,  /* Cprt */
189     FALSE,  /* Limb */
190     FALSE,  /* Linb */
191     FALSE,  /* Osma */
192     FALSE,  /* Shaw */
193     FALSE,  /* Tale */
194     FALSE,  /* Ugar */
195     FALSE,  /* Hrkt */
196     FALSE,  /* Bugi */
197     FALSE,  /* Glag */
198     FALSE,  /* Khar */
199     FALSE,  /* Sylo */
200     FALSE,  /* Talu */
201     FALSE,  /* Tfng */
202     FALSE,  /* Xpeo */
203     FALSE,  /* Bali */
204     FALSE,  /* Batk */
205     FALSE,  /* Blis */
206     FALSE,  /* Brah */
207     FALSE,  /* Cham */
208     FALSE,  /* Cirt */
209     FALSE,  /* Cyrs */
210     FALSE,  /* Egyd */
211     FALSE,  /* Egyh */
212     FALSE,  /* Egyp */
213     FALSE,  /* Geok */
214     FALSE,  /* Hans */
215     FALSE,  /* Hant */
216     FALSE,  /* Hmng */
217     FALSE,  /* Hung */
218     FALSE,  /* Inds */
219     FALSE,  /* Java */
220     FALSE,  /* Kali */
221     FALSE,  /* Latf */
222     FALSE,  /* Latg */
223     FALSE,  /* Lepc */
224     FALSE,  /* Lina */
225     FALSE,  /* Mand */
226     FALSE,  /* Maya */
227     FALSE,  /* Mero */
228     FALSE,  /* Nkoo */
229     FALSE,  /* Orkh */
230     FALSE,  /* Perm */
231     FALSE,  /* Phag */
232     FALSE,  /* Phnx */
233     FALSE,  /* Plrd */
234     FALSE,  /* Roro */
235     FALSE,  /* Sara */
236     FALSE,  /* Syre */
237     FALSE,  /* Syrj */
238     FALSE,  /* Syrn */
239     FALSE,  /* Teng */
240     FALSE,  /* Taii */
241     FALSE,  /* Visp */
242     FALSE,  /* Xsux */
243     FALSE,  /* Zxxx */
244     FALSE,  /* Zzzz */
245     FALSE,  /* Cari */
246     FALSE,  /* Jpan */
247     FALSE,  /* Lana */
248     FALSE,  /* Lyci */
249     FALSE,  /* Lydi */
250     FALSE,  /* Olck */
251     FALSE,  /* Rjng */
252     FALSE,  /* Saur */
253     FALSE,  /* Sgnw */
254     FALSE,  /* Sund */
255     FALSE,  /* Moon */
256     FALSE,  /* Mtei */
257     FALSE,  /* Armi */
258     FALSE,  /* Avst */
259     FALSE,  /* Cakm */
260     FALSE,  /* Kore */
261     FALSE,  /* Kthi */
262     FALSE,  /* Mani */
263     FALSE,  /* Phli */
264     FALSE,  /* Phlp */
265     FALSE,  /* Phlv */
266     FALSE,  /* Prti */
267     FALSE,  /* Samr */
268     FALSE,  /* Tavt */
269     FALSE,  /* Zmth */
270     FALSE,  /* Zsym */
271     FALSE,  /* Bamu */
272     FALSE,  /* Lisu */
273     FALSE,  /* Nkgb */
274     FALSE   /* Sarb */
275 };
276
277
278 const char ParagraphLayout::fgClassID = 0;
279
280 static void fillMissingCharToGlyphMapValues(le_int32 *charToGlyphMap,
281                                             le_int32 charCount) {
282     le_int32 lastValidGlyph = -1;
283     le_int32 ch;
284     for (ch = 0; ch <= charCount; ch += 1) {
285         if (charToGlyphMap[ch] == -1) {
286             charToGlyphMap[ch] = lastValidGlyph;
287         } else {
288             lastValidGlyph = charToGlyphMap[ch];
289         }
290     }
291 }
292
293 /*
294  * How to deal with composite fonts:
295  *
296  * Don't store the client's FontRuns; we'll need to compute sub-font FontRuns using Doug's
297  * LEFontInstance method. Do that by intersecting the client's FontRuns with fScriptRuns. Use
298  * that to compute fFontRuns, and then intersect fFontRuns, fScriptRuns and fLevelRuns. Doing
299  * it in this order means we do a two-way intersection and a three-way intersection.
300  *
301  * An optimization would be to only do this if there's at least one composite font...
302  *
303  * Other notes:
304  *
305  * * Return the sub-fonts as the run fonts... could keep the mapping back to the client's FontRuns
306  *   but that probably makes it more complicated of everyone...
307  *
308  * * Take the LineInfo and LineRun types from Paragraph and use them here, incorporate them into the API.
309  *
310  * * Might want to change the name of the StyleRun type, and make a new one that holds fonts, scripts and levels?
311  *
312  */
313 ParagraphLayout::ParagraphLayout(const LEUnicode chars[], le_int32 count,
314                                  const FontRuns   *fontRuns,
315                                  const ValueRuns  *levelRuns,
316                                  const ValueRuns  *scriptRuns,
317                                  const LocaleRuns *localeRuns,
318                                  UBiDiLevel paragraphLevel, le_bool vertical,
319                                  LEErrorCode &status)
320                                  : fChars(chars), fCharCount(count),
321                                    fFontRuns(NULL), fLevelRuns(levelRuns), fScriptRuns(scriptRuns), fLocaleRuns(localeRuns),
322                                    fVertical(vertical), fClientLevels(TRUE), fClientScripts(TRUE), fClientLocales(TRUE), fEmbeddingLevels(NULL),
323                                    fAscent(0), fDescent(0), fLeading(0),
324                                    fGlyphToCharMap(NULL), fCharToMinGlyphMap(NULL), fCharToMaxGlyphMap(NULL), fGlyphWidths(NULL), fGlyphCount(0),
325                                    fParaBidi(NULL), fLineBidi(NULL),
326                                    fStyleRunLimits(NULL), fStyleIndices(NULL), fStyleRunCount(0),
327                                    fBreakIterator(NULL), fLineStart(-1), fLineEnd(0),
328                                  /*fVisualRuns(NULL), fStyleRunInfo(NULL), fVisualRunCount(-1),
329                                    fFirstVisualRun(-1), fLastVisualRun(-1),*/ fVisualRunLastX(0), fVisualRunLastY(0)
330 {
331
332     if (LE_FAILURE(status)) {
333         fCharCount = -1;
334         return;
335     }
336
337     (void)copyright;  // Suppress unused variable warning.
338     (void)fVertical;  // Suppress warning for unused field fVertical.
339
340     // FIXME: should check the limit arrays for consistency...
341
342     computeLevels(paragraphLevel);
343
344     if (scriptRuns == NULL) {
345         computeScripts();
346     }
347
348     if (localeRuns == NULL) {
349         computeLocales();
350     }
351
352     computeSubFonts(fontRuns, status);
353
354     if (LE_FAILURE(status)) {
355         //other stuff?
356         fCharCount = -1;
357         return;
358     }
359
360     // now intersect the font, direction and script runs...
361     const RunArray *styleRunArrays[] = {fFontRuns, fLevelRuns, fScriptRuns, fLocaleRuns};
362     le_int32  styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0];
363     StyleRuns styleRuns(styleRunArrays, styleCount);
364     LEErrorCode layoutStatus = LE_NO_ERROR;
365
366     fStyleRunCount = styleRuns.getRuns(NULL, NULL);
367
368     fStyleRunLimits = LE_NEW_ARRAY(le_int32, fStyleRunCount);
369     fStyleIndices   = LE_NEW_ARRAY(le_int32, fStyleRunCount * styleCount);
370     if ((fStyleRunLimits == NULL) || (fStyleIndices == NULL)) {
371         status = LE_MEMORY_ALLOCATION_ERROR;
372         return;
373     }
374
375     styleRuns.getRuns(fStyleRunLimits, fStyleIndices);
376
377     // now build a LayoutEngine for each style run...
378     le_int32 *styleIndices = fStyleIndices;
379     le_int32 run, runStart;
380
381     fStyleRunInfo = LE_NEW_ARRAY(StyleRunInfo, fStyleRunCount);
382     if (fStyleRunInfo == NULL) {
383         status = LE_MEMORY_ALLOCATION_ERROR;
384         return;
385     }
386     else {
387         // initialize
388         for (run = 0; run < fStyleRunCount; run += 1) {
389             fStyleRunInfo[run].font = NULL;
390             fStyleRunInfo[run].runBase = 0;
391             fStyleRunInfo[run].runLimit = 0;
392             fStyleRunInfo[run].script = (UScriptCode)0;
393             fStyleRunInfo[run].locale = NULL;
394             fStyleRunInfo[run].level = 0;
395             fStyleRunInfo[run].glyphBase = 0;
396             fStyleRunInfo[run].engine = NULL;
397             fStyleRunInfo[run].glyphCount = 0;
398             fStyleRunInfo[run].glyphs = NULL;
399             fStyleRunInfo[run].positions = NULL;
400         }
401     }
402
403     fGlyphCount = 0;
404     for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
405         fStyleRunInfo[run].font      = fFontRuns->getFont(styleIndices[0]);
406         fStyleRunInfo[run].runBase   = runStart;
407         fStyleRunInfo[run].runLimit  = fStyleRunLimits[run];
408         fStyleRunInfo[run].script    = (UScriptCode) fScriptRuns->getValue(styleIndices[2]);
409         fStyleRunInfo[run].locale    = fLocaleRuns->getLocale(styleIndices[3]);
410         fStyleRunInfo[run].level     = (UBiDiLevel) fLevelRuns->getValue(styleIndices[1]);
411         fStyleRunInfo[run].glyphBase = fGlyphCount;
412
413         fStyleRunInfo[run].engine = LayoutEngine::layoutEngineFactory(fStyleRunInfo[run].font,
414             fStyleRunInfo[run].script, getLanguageCode(fStyleRunInfo[run].locale), layoutStatus);
415         if (LE_FAILURE(layoutStatus)) {
416             status = layoutStatus;
417             return;
418         }
419
420         fStyleRunInfo[run].glyphCount = fStyleRunInfo[run].engine->layoutChars(fChars, runStart, fStyleRunLimits[run] - runStart, fCharCount,
421             fStyleRunInfo[run].level & 1, 0, 0, layoutStatus);
422         if (LE_FAILURE(layoutStatus)) {
423             status = layoutStatus;
424             return;
425         }
426
427         runStart = fStyleRunLimits[run];
428         styleIndices += styleCount;
429         fGlyphCount += fStyleRunInfo[run].glyphCount;
430     }
431
432     // Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps,
433     // in logical order. (Both maps need an extra entry for the end of the text.)
434     //
435     // For each layout get the positions and convert them into glyph widths, in
436     // logical order. Get the glyph-to-char mapping, offset by starting index in the
437     // character array. Swap the glyph width and glyph-to-char arrays into logical order.
438     // Finally, fill in the char-to-glyph mappings.
439     fGlyphWidths       = LE_NEW_ARRAY(float, fGlyphCount);
440     fGlyphToCharMap    = LE_NEW_ARRAY(le_int32, fGlyphCount + 1);
441     fCharToMinGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
442     fCharToMaxGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
443     if ((fGlyphWidths == NULL) || (fGlyphToCharMap == NULL) ||
444         (fCharToMinGlyphMap == NULL) || (fCharToMaxGlyphMap == NULL)) {
445         status = LE_MEMORY_ALLOCATION_ERROR;
446         return;
447     }
448
449     le_int32 glyph;
450
451     for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
452         LayoutEngine *engine = fStyleRunInfo[run].engine;
453         le_int32 glyphCount  = fStyleRunInfo[run].glyphCount;
454         le_int32 glyphBase   = fStyleRunInfo[run].glyphBase;
455
456         fStyleRunInfo[run].glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount);
457         fStyleRunInfo[run].positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2);
458         if ((fStyleRunInfo[run].glyphs == NULL) ||
459             (fStyleRunInfo[run].positions == NULL)) {
460             status = LE_MEMORY_ALLOCATION_ERROR;
461             return;
462         }
463
464         engine->getGlyphs(fStyleRunInfo[run].glyphs, layoutStatus);
465         if (LE_FAILURE(layoutStatus)) {
466             status = layoutStatus;
467             return;
468         }
469
470         engine->getGlyphPositions(fStyleRunInfo[run].positions, layoutStatus);
471         if (LE_FAILURE(layoutStatus)) {
472             status = layoutStatus;
473             return;
474         }
475
476         engine->getCharIndices(&fGlyphToCharMap[glyphBase], runStart, layoutStatus);
477         if (LE_FAILURE(layoutStatus)) {
478             status = layoutStatus;
479             return;
480         }
481
482         for (glyph = 0; glyph < glyphCount; glyph += 1) {
483             fGlyphWidths[glyphBase + glyph] = fStyleRunInfo[run].positions[glyph * 2 + 2] - fStyleRunInfo[run].positions[glyph * 2];
484         }
485
486         if ((fStyleRunInfo[run].level & 1) != 0) {
487             LXUtilities::reverse(&fGlyphWidths[glyphBase], glyphCount);
488             LXUtilities::reverse(&fGlyphToCharMap[glyphBase], glyphCount);
489         }
490
491         runStart = fStyleRunLimits[run];
492
493         delete engine;
494         fStyleRunInfo[run].engine = NULL;
495     }
496
497     fGlyphToCharMap[fGlyphCount] = fCharCount;
498
499     // Initialize the char-to-glyph maps to -1 so that we can later figure out
500     // whether any of the entries in the map aren't filled in below.
501     le_int32 chIndex;
502     for (chIndex = 0; chIndex <= fCharCount; chIndex += 1) {
503         fCharToMinGlyphMap[chIndex] = -1;
504         fCharToMaxGlyphMap[chIndex] = -1;
505     }
506
507     for (glyph = fGlyphCount - 1; glyph >= 0; glyph -= 1) {
508         le_int32 ch = fGlyphToCharMap[glyph];
509
510         fCharToMinGlyphMap[ch] = glyph;
511     }
512
513     fCharToMinGlyphMap[fCharCount] = fGlyphCount;
514
515     for (glyph = 0; glyph < fGlyphCount; glyph += 1) {
516         le_int32 ch = fGlyphToCharMap[glyph];
517
518         fCharToMaxGlyphMap[ch] = glyph;
519     }
520
521     fCharToMaxGlyphMap[fCharCount] = fGlyphCount;
522
523     // Now fill in the missing values in the char-to-glyph maps.
524     fillMissingCharToGlyphMapValues(fCharToMinGlyphMap, fCharCount);
525     fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap, fCharCount);
526 }
527
528 ParagraphLayout::~ParagraphLayout()
529 {
530     delete (FontRuns *) fFontRuns;
531
532     if (! fClientLevels) {
533         delete (ValueRuns *) fLevelRuns;
534         fLevelRuns = NULL;
535
536         fClientLevels = TRUE;
537     }
538
539     if (! fClientScripts) {
540         delete (ValueRuns *) fScriptRuns;
541         fScriptRuns = NULL;
542
543         fClientScripts = TRUE;
544     }
545
546     if (! fClientLocales) {
547         delete (LocaleRuns *) fLocaleRuns;
548         fLocaleRuns = NULL;
549
550         fClientLocales = TRUE;
551     }
552
553     if (fEmbeddingLevels != NULL) {
554         LE_DELETE_ARRAY(fEmbeddingLevels);
555         fEmbeddingLevels = NULL;
556     }
557
558     if (fGlyphToCharMap != NULL) {
559         LE_DELETE_ARRAY(fGlyphToCharMap);
560         fGlyphToCharMap = NULL;
561     }
562
563     if (fCharToMinGlyphMap != NULL) {
564         LE_DELETE_ARRAY(fCharToMinGlyphMap);
565         fCharToMinGlyphMap = NULL;
566     }
567
568     if (fCharToMaxGlyphMap != NULL) {
569         LE_DELETE_ARRAY(fCharToMaxGlyphMap);
570         fCharToMaxGlyphMap = NULL;
571     }
572
573     if (fGlyphWidths != NULL) {
574         LE_DELETE_ARRAY(fGlyphWidths);
575         fGlyphWidths = NULL;
576     }
577
578     if (fParaBidi != NULL) {
579         ubidi_close(fParaBidi);
580         fParaBidi = NULL;
581     }
582
583     if (fLineBidi != NULL) {
584         ubidi_close(fLineBidi);
585         fLineBidi = NULL;
586     }
587
588     if (fStyleRunCount > 0) {
589         le_int32 run;
590
591         LE_DELETE_ARRAY(fStyleRunLimits);
592         LE_DELETE_ARRAY(fStyleIndices);
593
594         for (run = 0; run < fStyleRunCount; run += 1) {
595             LE_DELETE_ARRAY(fStyleRunInfo[run].glyphs);
596             LE_DELETE_ARRAY(fStyleRunInfo[run].positions);
597
598             fStyleRunInfo[run].glyphs    = NULL;
599             fStyleRunInfo[run].positions = NULL;
600         }
601
602         LE_DELETE_ARRAY(fStyleRunInfo);
603
604         fStyleRunLimits = NULL;
605         fStyleIndices   = NULL;
606         fStyleRunInfo        = NULL;
607         fStyleRunCount  = 0;
608     }
609
610     if (fBreakIterator != NULL) {
611         delete fBreakIterator;
612         fBreakIterator = NULL;
613     }
614 }
615
616
617 le_bool ParagraphLayout::isComplex(const LEUnicode chars[], le_int32 count)
618 {
619     UErrorCode scriptStatus = U_ZERO_ERROR;
620     UScriptCode scriptCode  = USCRIPT_INVALID_CODE;
621     UScriptRun *sr = uscript_openRun(chars, count, &scriptStatus);
622     le_bool result = FALSE;
623
624     while (uscript_nextRun(sr, NULL, NULL, &scriptCode)) {
625         if (isComplex(scriptCode)) {
626             result = TRUE;
627             break;
628         }
629     }
630
631     uscript_closeRun(sr);
632     return result;
633 }
634
635 le_int32 ParagraphLayout::getAscent() const
636 {
637     if (fAscent <= 0 && fCharCount > 0) {
638         ((ParagraphLayout *) this)->computeMetrics();
639     }
640
641     return fAscent;
642 }
643
644 le_int32 ParagraphLayout::getDescent() const
645 {
646     if (fAscent <= 0 && fCharCount > 0) {
647         ((ParagraphLayout *) this)->computeMetrics();
648     }
649
650     return fDescent;
651 }
652
653 le_int32 ParagraphLayout::getLeading() const
654 {
655     if (fAscent <= 0 && fCharCount > 0) {
656         ((ParagraphLayout *) this)->computeMetrics();
657     }
658
659     return fLeading;
660 }
661
662 le_bool ParagraphLayout::isDone() const
663 {
664     return fLineEnd >= fCharCount;
665 }
666
667 ParagraphLayout::Line *ParagraphLayout::nextLine(float width)
668 {
669     if (isDone()) {
670         return NULL;
671     }
672
673     fLineStart = fLineEnd;
674
675     if (width > 0) {
676         le_int32 glyph    = fCharToMinGlyphMap[fLineStart];
677         float widthSoFar  = 0;
678
679         while (glyph < fGlyphCount && widthSoFar + fGlyphWidths[glyph] <= width) {
680             widthSoFar += fGlyphWidths[glyph++];
681         }
682
683         // If no glyphs fit on the line, force one to fit.
684         //
685         // (There shouldn't be any zero width glyphs at the
686         // start of a line unless the paragraph consists of
687         // only zero width glyphs, because otherwise the zero
688         // width glyphs will have been included on the end of
689         // the previous line...)
690         if (widthSoFar == 0 && glyph < fGlyphCount) {
691             glyph += 1;
692         }
693
694         fLineEnd = previousBreak(fGlyphToCharMap[glyph]);
695
696         // If this break is at or before the last one,
697         // find a glyph, starting at the one which didn't
698         // fit, that produces a break after the last one.
699         while (fLineEnd <= fLineStart) {
700             fLineEnd = fGlyphToCharMap[glyph++];
701         }
702     } else {
703         fLineEnd = fCharCount;
704     }
705
706     return computeVisualRuns();
707 }
708
709 void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel)
710 {
711     UErrorCode bidiStatus = U_ZERO_ERROR;
712
713     if (fLevelRuns != NULL) {
714         le_int32 ch;
715         le_int32 run;
716
717         fEmbeddingLevels = LE_NEW_ARRAY(UBiDiLevel, fCharCount);
718
719         for (ch = 0, run = 0; run < fLevelRuns->getCount(); run += 1) {
720             UBiDiLevel runLevel = (UBiDiLevel) fLevelRuns->getValue(run) | UBIDI_LEVEL_OVERRIDE;
721             le_int32   runLimit = fLevelRuns->getLimit(run);
722
723             while (ch < runLimit) {
724                 fEmbeddingLevels[ch++] = runLevel;
725             }
726         }
727     }
728
729     fParaBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
730     ubidi_setPara(fParaBidi, fChars, fCharCount, paragraphLevel, fEmbeddingLevels, &bidiStatus);
731
732     if (fLevelRuns == NULL) {
733         le_int32 levelRunCount = ubidi_countRuns(fParaBidi, &bidiStatus);
734         ValueRuns *levelRuns = new ValueRuns(levelRunCount);
735
736         le_int32 logicalStart = 0;
737         le_int32 run;
738         le_int32 limit;
739         UBiDiLevel level;
740
741         for (run = 0; run < levelRunCount; run += 1) {
742             ubidi_getLogicalRun(fParaBidi, logicalStart, &limit, &level);
743             levelRuns->add(level, limit);
744             logicalStart = limit;
745         }
746
747         fLevelRuns    = levelRuns;
748         fClientLevels = FALSE;
749     }
750 }
751
752 void ParagraphLayout::computeScripts()
753 {
754     UErrorCode scriptStatus = U_ZERO_ERROR;
755     UScriptRun *sr = uscript_openRun(fChars, fCharCount, &scriptStatus);
756     ValueRuns  *scriptRuns = new ValueRuns(0);
757     le_int32 limit;
758     UScriptCode script;
759
760     while (uscript_nextRun(sr, NULL, &limit, &script)) {
761         scriptRuns->add(script, limit);
762     }
763
764     uscript_closeRun(sr);
765
766     fScriptRuns    = scriptRuns;
767     fClientScripts = FALSE;
768 }
769
770 void ParagraphLayout::computeLocales()
771 {
772     LocaleRuns *localeRuns = new LocaleRuns(0);
773     const Locale *defaultLocale = &Locale::getDefault();
774
775     localeRuns->add(defaultLocale, fCharCount);
776
777     fLocaleRuns    = localeRuns;
778     fClientLocales = FALSE;
779 }
780
781 void ParagraphLayout::computeSubFonts(const FontRuns *fontRuns, LEErrorCode &status)
782 {
783     if (LE_FAILURE(status)) {
784         return;
785     }
786
787     const RunArray *styleRunArrays[] = {fontRuns, fScriptRuns};
788     le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0];
789     StyleRuns styleRuns(styleRunArrays, styleCount);
790     le_int32 styleRunCount = styleRuns.getRuns(NULL, NULL);
791     le_int32 *styleRunLimits = LE_NEW_ARRAY(le_int32, styleRunCount);
792     le_int32 *styleIndices = LE_NEW_ARRAY(le_int32, styleRunCount * styleCount);
793     FontRuns *subFontRuns  = new FontRuns(0);
794     le_int32  run, offset, *si;
795
796     styleRuns.getRuns(styleRunLimits, styleIndices);
797
798     si = styleIndices;
799     offset = 0;
800
801     for (run = 0; run < styleRunCount; run += 1) {
802         const LEFontInstance *runFont = fontRuns->getFont(si[0]);
803         le_int32 script = fScriptRuns->getValue(si[1]);
804
805         while (offset < styleRunLimits[run]) {
806             const LEFontInstance *subFont = runFont->getSubFont(fChars, &offset, styleRunLimits[run], script, status);
807
808             if (LE_FAILURE(status)) {
809                 delete subFontRuns;
810                 goto cleanUp;
811             }
812
813             subFontRuns->add(subFont, offset);
814         }
815
816         si += styleCount;
817     }
818
819     fFontRuns = subFontRuns;
820
821 cleanUp:
822     LE_DELETE_ARRAY(styleIndices);
823     LE_DELETE_ARRAY(styleRunLimits);
824 }
825
826 void ParagraphLayout::computeMetrics()
827 {
828     le_int32 i, count = fFontRuns->getCount();
829     le_int32 maxDL = 0;
830
831     for (i = 0; i < count; i += 1) {
832         const LEFontInstance *font = fFontRuns->getFont(i);
833         le_int32 ascent  = font->getAscent();
834         le_int32 descent = font->getDescent();
835         le_int32 leading = font->getLeading();
836         le_int32 dl      = descent + leading;
837
838         if (ascent > fAscent) {
839             fAscent = ascent;
840         }
841
842         if (descent > fDescent) {
843             fDescent = descent;
844         }
845
846         if (leading > fLeading) {
847             fLeading = leading;
848         }
849
850         if (dl > maxDL) {
851             maxDL = dl;
852         }
853     }
854
855     fLeading = maxDL - fDescent;
856 }
857
858 #if 1
859 struct LanguageMap
860 {
861     const char *localeCode;
862     le_int32 languageCode;
863 };
864
865 static const LanguageMap languageMap[] =
866 {
867     {"afr", afkLanguageCode}, // Afrikaans
868     {"ara", araLanguageCode}, // Arabic
869     {"asm", asmLanguageCode}, // Assamese
870     {"bel", belLanguageCode}, // Belarussian
871     {"ben", benLanguageCode}, // Bengali
872     {"bod", tibLanguageCode}, // Tibetan
873     {"bul", bgrLanguageCode}, // Bulgarian
874     {"cat", catLanguageCode}, // Catalan
875     {"ces", csyLanguageCode}, // Czech
876     {"che", cheLanguageCode}, // Chechen
877     {"cop", copLanguageCode}, // Coptic
878     {"cym", welLanguageCode}, // Welsh
879     {"dan", danLanguageCode}, // Danish
880     {"deu", deuLanguageCode}, // German
881     {"dzo", dznLanguageCode}, // Dzongkha
882     {"ell", ellLanguageCode}, // Greek
883     {"eng", engLanguageCode}, // English
884     {"est", etiLanguageCode}, // Estonian
885     {"eus", euqLanguageCode}, // Basque
886     {"fas", farLanguageCode}, // Farsi
887     {"fin", finLanguageCode}, // Finnish
888     {"fra", fraLanguageCode}, // French
889     {"gle", gaeLanguageCode}, // Irish Gaelic
890     {"guj", gujLanguageCode}, // Gujarati
891     {"hau", hauLanguageCode}, // Hausa
892     {"heb", iwrLanguageCode}, // Hebrew
893     {"hin", hinLanguageCode}, // Hindi
894     {"hrv", hrvLanguageCode}, // Croatian
895     {"hun", hunLanguageCode}, // Hungarian
896     {"hye", hyeLanguageCode}, // Armenian
897     {"ind", indLanguageCode}, // Indonesian
898     {"ita", itaLanguageCode}, // Italian
899     {"jpn", janLanguageCode}, // Japanese
900     {"kan", kanLanguageCode}, // Kannada
901     {"kas", kshLanguageCode}, // Kashmiri
902     {"khm", khmLanguageCode}, // Khmer
903     {"kok", kokLanguageCode}, // Konkani
904     {"kor", korLanguageCode}, // Korean
905 //  {"mal_XXX", malLanguageCode}, // Malayalam - Traditional
906     {"mal", mlrLanguageCode}, // Malayalam - Reformed
907     {"mar", marLanguageCode}, // Marathi
908     {"mlt", mtsLanguageCode}, // Maltese
909     {"mni", mniLanguageCode}, // Manipuri
910     {"mon", mngLanguageCode}, // Mongolian
911     {"nep", nepLanguageCode}, // Nepali
912     {"ori", oriLanguageCode}, // Oriya
913     {"pol", plkLanguageCode}, // Polish
914     {"por", ptgLanguageCode}, // Portuguese
915     {"pus", pasLanguageCode}, // Pashto
916     {"ron", romLanguageCode}, // Romanian
917     {"rus", rusLanguageCode}, // Russian
918     {"san", sanLanguageCode}, // Sanskrit
919     {"sin", snhLanguageCode}, // Sinhalese
920     {"slk", skyLanguageCode}, // Slovak
921     {"snd", sndLanguageCode}, // Sindhi
922     {"slv", slvLanguageCode}, // Slovenian
923     {"spa", espLanguageCode}, // Spanish
924     {"sqi", sqiLanguageCode}, // Albanian
925     {"srp", srbLanguageCode}, // Serbian
926     {"swe", sveLanguageCode}, // Swedish
927     {"syr", syrLanguageCode}, // Syriac
928     {"tam", tamLanguageCode}, // Tamil
929     {"tel", telLanguageCode}, // Telugu
930     {"tha", thaLanguageCode}, // Thai
931     {"tur", trkLanguageCode}, // Turkish
932     {"urd", urdLanguageCode}, // Urdu
933     {"yid", jiiLanguageCode}, // Yiddish
934 //  {"zhp", zhpLanguageCode}, // Chinese - Phonetic
935     {"zho", zhsLanguageCode}, // Chinese
936     {"zho_CHN", zhsLanguageCode}, // Chinese - China
937     {"zho_HKG", zhsLanguageCode}, // Chinese - Hong Kong
938     {"zho_MAC", zhtLanguageCode}, // Chinese - Macao
939     {"zho_SGP", zhsLanguageCode}, // Chinese - Singapore
940     {"zho_TWN", zhtLanguageCode}  // Chinese - Taiwan
941 };
942
943 static const le_int32 languageMapCount = ARRAY_SIZE(languageMap);
944
945 le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
946 {
947     char code[8] = {0, 0, 0, 0, 0, 0, 0, 0};
948     const char *language = locale->getISO3Language();
949     const char *country  = locale->getISO3Country();
950
951     uprv_strcat(code, language);
952
953     if ((uprv_strcmp(language, "zho") == 0) && country != NULL) {
954         uprv_strcat(code, "_");
955         uprv_strcat(code, country);
956     }
957
958     for (le_int32 i = 0; i < languageMapCount; i += 1) {
959         if (uprv_strcmp(code, languageMap[i].localeCode) == 0) {
960             return languageMap[i].languageCode;
961         }
962     }
963
964     return nullLanguageCode;
965 }
966 #else
967
968 // TODO - dummy implementation for right now...
969 le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
970 {
971     return nullLanguageCode;
972 }
973 #endif
974
975 le_bool ParagraphLayout::isComplex(UScriptCode script)
976 {
977     if (script < 0 || script >= (UScriptCode) scriptCodeCount) {
978         return FALSE;
979     }
980
981     return complexTable[script];
982 }
983
984 le_int32 ParagraphLayout::previousBreak(le_int32 charIndex)
985 {
986     // skip over any whitespace or control characters,
987     // because they can hang in the margin.
988     while (charIndex < fCharCount &&
989            (u_isWhitespace(fChars[charIndex]) ||
990             u_iscntrl(fChars[charIndex]))) {
991         charIndex += 1;
992     }
993
994     // Create the BreakIterator if we don't already have one
995     if (fBreakIterator == NULL) {
996         Locale thai("th");
997         UCharCharacterIterator *iter = new UCharCharacterIterator(fChars, fCharCount);
998         UErrorCode status = U_ZERO_ERROR;
999
1000         fBreakIterator = BreakIterator::createLineInstance(thai, status);
1001         fBreakIterator->adoptText(iter);
1002     }
1003
1004     // return the break location that's at or before
1005     // the character we stopped on. Note: if we're
1006     // on a break, the "+ 1" will cause preceding to
1007     // back up to it.
1008     return fBreakIterator->preceding(charIndex + 1);
1009 }
1010
1011 ParagraphLayout::Line *ParagraphLayout::computeVisualRuns()
1012 {
1013     UErrorCode bidiStatus = U_ZERO_ERROR;
1014     le_int32 dirRunCount, visualRun;
1015
1016     fVisualRunLastX = 0;
1017     fVisualRunLastY = 0;
1018     fFirstVisualRun = getCharRun(fLineStart);
1019     fLastVisualRun  = getCharRun(fLineEnd - 1);
1020
1021     if (fLineBidi == NULL) {
1022         fLineBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
1023     }
1024
1025     ubidi_setLine(fParaBidi, fLineStart, fLineEnd, fLineBidi, &bidiStatus);
1026     dirRunCount = ubidi_countRuns(fLineBidi, &bidiStatus);
1027
1028     Line *line = new Line();
1029
1030     for (visualRun = 0; visualRun < dirRunCount; visualRun += 1) {
1031         le_int32 relStart, run, runLength;
1032         UBiDiDirection runDirection = ubidi_getVisualRun(fLineBidi, visualRun, &relStart, &runLength);
1033         le_int32 runStart = fLineStart + relStart;
1034         le_int32 runEnd   = runStart + runLength - 1;
1035         le_int32 firstRun = getCharRun(runStart);
1036         le_int32 lastRun  = getCharRun(runEnd);
1037         le_int32 startRun = (runDirection == UBIDI_LTR)? firstRun : lastRun;
1038         le_int32 stopRun  = (runDirection == UBIDI_LTR)? lastRun + 1 : firstRun - 1;
1039         le_int32 dir      = (runDirection == UBIDI_LTR)?  1 : -1;
1040
1041         for (run = startRun; run != stopRun; run += dir) {
1042             le_int32 firstChar = (run == firstRun)? runStart : fStyleRunInfo[run].runBase;
1043             le_int32 lastChar  = (run == lastRun)?  runEnd   : fStyleRunInfo[run].runLimit - 1;
1044
1045             appendRun(line, run, firstChar, lastChar);
1046         }
1047     }
1048
1049     return line;
1050 }
1051
1052 void ParagraphLayout::appendRun(ParagraphLayout::Line *line, le_int32 run, le_int32 firstChar, le_int32 lastChar)
1053 {
1054     le_int32 glyphBase = fStyleRunInfo[run].glyphBase;
1055     le_int32 inGlyph, outGlyph;
1056
1057     // Get the glyph indices for all the characters between firstChar and lastChar,
1058     // make the minimum one be leftGlyph and the maximum one be rightGlyph.
1059     // (need to do this to handle local reorderings like Indic left matras)
1060     le_int32 leftGlyph  = fGlyphCount;
1061     le_int32 rightGlyph = -1;
1062     le_int32 ch;
1063
1064     for (ch = firstChar; ch <= lastChar; ch += 1) {
1065         le_int32 minGlyph = fCharToMinGlyphMap[ch];
1066         le_int32 maxGlyph = fCharToMaxGlyphMap[ch];
1067
1068         if (minGlyph < leftGlyph) {
1069             leftGlyph = minGlyph;
1070         }
1071
1072         if (maxGlyph > rightGlyph) {
1073             rightGlyph = maxGlyph;
1074         }
1075     }
1076
1077     if ((fStyleRunInfo[run].level & 1) != 0) {
1078         le_int32 swap = rightGlyph;
1079         le_int32 last = glyphBase + fStyleRunInfo[run].glyphCount - 1;
1080
1081         // Here, we want to remove the glyphBase bias...
1082         rightGlyph = last - leftGlyph;
1083         leftGlyph  = last - swap;
1084     } else {
1085         rightGlyph -= glyphBase;
1086         leftGlyph  -= glyphBase;
1087     }
1088
1089     // Set the position bias for the glyphs. If we're at the start of
1090     // a line, we want the first glyph to be at x = 0, even if it comes
1091     // from the middle of a layout. If we've got a right-to-left run, we
1092     // want the left-most glyph to start at the final x position of the
1093     // previous run, even though this glyph may be in the middle of the
1094     // run.
1095     fVisualRunLastX -= fStyleRunInfo[run].positions[leftGlyph * 2];
1096
1097     // Make rightGlyph be the glyph just to the right of
1098     // the run's glyphs
1099     rightGlyph += 1;
1100
1101     UBiDiDirection direction  = ((fStyleRunInfo[run].level & 1) == 0)? UBIDI_LTR : UBIDI_RTL;
1102     le_int32   glyphCount     = rightGlyph - leftGlyph;
1103     LEGlyphID *glyphs         = LE_NEW_ARRAY(LEGlyphID, glyphCount);
1104     float     *positions      = LE_NEW_ARRAY(float, glyphCount * 2 + 2);
1105     le_int32  *glyphToCharMap = LE_NEW_ARRAY(le_int32, glyphCount);
1106
1107     LE_ARRAY_COPY(glyphs, &fStyleRunInfo[run].glyphs[leftGlyph], glyphCount);
1108
1109     for (outGlyph = 0, inGlyph = leftGlyph * 2; inGlyph <= rightGlyph * 2; inGlyph += 2, outGlyph += 2) {
1110         positions[outGlyph]     = fStyleRunInfo[run].positions[inGlyph] + fVisualRunLastX;
1111         positions[outGlyph + 1] = fStyleRunInfo[run].positions[inGlyph + 1] + fVisualRunLastY;
1112     }
1113
1114     // Save the ending position of this run
1115     // to use for the start of the next run
1116     fVisualRunLastX = positions[outGlyph - 2];
1117     fVisualRunLastY = positions[outGlyph - 1];
1118
1119     if ((fStyleRunInfo[run].level & 1) == 0) {
1120         for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) {
1121             glyphToCharMap[outGlyph] = fGlyphToCharMap[glyphBase + inGlyph];
1122         }
1123     } else {
1124         // Because fGlyphToCharMap is stored in logical order to facilitate line breaking,
1125         // we need to map the physical glyph indices to logical indices while we copy the
1126         // character indices.
1127         le_int32 base = glyphBase + fStyleRunInfo[run].glyphCount - 1;
1128
1129         for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) {
1130             glyphToCharMap[outGlyph] = fGlyphToCharMap[base - inGlyph];
1131         }
1132     }
1133
1134     line->append(fStyleRunInfo[run].font, direction, glyphCount, glyphs, positions, glyphToCharMap);
1135 }
1136
1137 le_int32 ParagraphLayout::getCharRun(le_int32 charIndex)
1138 {
1139     if (charIndex < 0 || charIndex > fCharCount) {
1140         return -1;
1141     }
1142
1143     le_int32 run;
1144
1145     // NOTE: as long as fStyleRunLimits is well-formed
1146     // the above range check guarantees that we'll never
1147     // fall off the end of the array.
1148     run = 0;
1149     while (charIndex >= fStyleRunLimits[run]) {
1150         run += 1;
1151     }
1152
1153     return run;
1154 }
1155
1156
1157 const char ParagraphLayout::Line::fgClassID = 0;
1158
1159 #define INITIAL_RUN_CAPACITY 4
1160 #define RUN_CAPACITY_GROW_LIMIT 16
1161
1162 ParagraphLayout::Line::~Line()
1163 {
1164     le_int32 i;
1165
1166     for (i = 0; i < fRunCount; i += 1) {
1167         delete fRuns[i];
1168     }
1169
1170     LE_DELETE_ARRAY(fRuns);
1171 }
1172
1173 le_int32 ParagraphLayout::Line::getAscent() const
1174 {
1175     if (fAscent <= 0) {
1176         ((ParagraphLayout::Line *)this)->computeMetrics();
1177     }
1178
1179     return fAscent;
1180 }
1181
1182 le_int32 ParagraphLayout::Line::getDescent() const
1183 {
1184     if (fAscent <= 0) {
1185         ((ParagraphLayout::Line *)this)->computeMetrics();
1186     }
1187
1188     return fDescent;
1189 }
1190
1191 le_int32 ParagraphLayout::Line::getLeading() const
1192 {
1193     if (fAscent <= 0) {
1194         ((ParagraphLayout::Line *)this)->computeMetrics();
1195     }
1196
1197     return fLeading;
1198 }
1199
1200 le_int32 ParagraphLayout::Line::getWidth() const
1201 {
1202     const VisualRun *lastRun = getVisualRun(fRunCount - 1);
1203
1204     if (lastRun == NULL) {
1205         return 0;
1206     }
1207
1208     le_int32 glyphCount = lastRun->getGlyphCount();
1209     const float *positions = lastRun->getPositions();
1210
1211     return (le_int32) positions[glyphCount * 2];
1212 }
1213
1214 const ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(le_int32 runIndex) const
1215 {
1216     if (runIndex < 0 || runIndex >= fRunCount) {
1217         return NULL;
1218     }
1219
1220     return fRuns[runIndex];
1221 }
1222
1223 void ParagraphLayout::Line::append(const LEFontInstance *font, UBiDiDirection direction, le_int32 glyphCount,
1224                                    const LEGlyphID glyphs[], const float positions[], const le_int32 glyphToCharMap[])
1225 {
1226     if (fRunCount >= fRunCapacity) {
1227         if (fRunCapacity == 0) {
1228             fRunCapacity = INITIAL_RUN_CAPACITY;
1229             fRuns = LE_NEW_ARRAY(ParagraphLayout::VisualRun *, fRunCapacity);
1230         } else {
1231             fRunCapacity += (fRunCapacity < RUN_CAPACITY_GROW_LIMIT? fRunCapacity : RUN_CAPACITY_GROW_LIMIT);
1232             fRuns = (ParagraphLayout::VisualRun **) LE_GROW_ARRAY(fRuns, fRunCapacity);
1233         }
1234     }
1235
1236     fRuns[fRunCount++] = new ParagraphLayout::VisualRun(font, direction, glyphCount, glyphs, positions, glyphToCharMap);
1237 }
1238
1239 void ParagraphLayout::Line::computeMetrics()
1240 {
1241     le_int32 maxDL = 0;
1242
1243     for (le_int32 i = 0; i < fRunCount; i += 1) {
1244         le_int32 ascent  = fRuns[i]->getAscent();
1245         le_int32 descent = fRuns[i]->getDescent();
1246         le_int32 leading = fRuns[i]->getLeading();
1247         le_int32 dl      = descent + leading;
1248
1249         if (ascent > fAscent) {
1250             fAscent = ascent;
1251         }
1252
1253         if (descent > fDescent) {
1254             fDescent = descent;
1255         }
1256
1257         if (leading > fLeading) {
1258             fLeading = leading;
1259         }
1260
1261         if (dl > maxDL) {
1262             maxDL = dl;
1263         }
1264     }
1265
1266     fLeading = maxDL - fDescent;
1267 }
1268
1269 const char ParagraphLayout::VisualRun::fgClassID = 0;
1270
1271 ParagraphLayout::VisualRun::~VisualRun()
1272 {
1273     LE_DELETE_ARRAY(fGlyphToCharMap);
1274     LE_DELETE_ARRAY(fPositions);
1275     LE_DELETE_ARRAY(fGlyphs);
1276 }
1277
1278 U_NAMESPACE_END
1279
1280 #endif
1281