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