1/*
2 **********************************************************************
3 *   Copyright (C) 2002-2010, 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    // FIXME: should check the limit arrays for consistency...
336
337    computeLevels(paragraphLevel);
338
339    if (scriptRuns == NULL) {
340        computeScripts();
341    }
342
343    if (localeRuns == NULL) {
344        computeLocales();
345    }
346
347    computeSubFonts(fontRuns, status);
348
349    if (LE_FAILURE(status)) {
350        //other stuff?
351        fCharCount = -1;
352        return;
353    }
354
355    // now intersect the font, direction and script runs...
356    const RunArray *styleRunArrays[] = {fFontRuns, fLevelRuns, fScriptRuns, fLocaleRuns};
357    le_int32  styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0];
358    StyleRuns styleRuns(styleRunArrays, styleCount);
359    LEErrorCode layoutStatus = LE_NO_ERROR;
360
361    fStyleRunCount = styleRuns.getRuns(NULL, NULL);
362
363    fStyleRunLimits = LE_NEW_ARRAY(le_int32, fStyleRunCount);
364    fStyleIndices   = LE_NEW_ARRAY(le_int32, fStyleRunCount * styleCount);
365    if ((fStyleRunLimits == NULL) || (fStyleIndices == NULL)) {
366        status = LE_MEMORY_ALLOCATION_ERROR;
367        return;
368    }
369
370    styleRuns.getRuns(fStyleRunLimits, fStyleIndices);
371
372    // now build a LayoutEngine for each style run...
373    le_int32 *styleIndices = fStyleIndices;
374    le_int32 run, runStart;
375
376    fStyleRunInfo = LE_NEW_ARRAY(StyleRunInfo, fStyleRunCount);
377    if (fStyleRunInfo == NULL) {
378        status = LE_MEMORY_ALLOCATION_ERROR;
379        return;
380    }
381    else {
382        // initialize
383        for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
384            fStyleRunInfo[run].font = NULL;
385            fStyleRunInfo[run].runBase = 0;
386            fStyleRunInfo[run].runLimit = 0;
387            fStyleRunInfo[run].script = (UScriptCode)0;
388            fStyleRunInfo[run].locale = NULL;
389            fStyleRunInfo[run].level = 0;
390            fStyleRunInfo[run].glyphBase = 0;
391            fStyleRunInfo[run].engine = NULL;
392            fStyleRunInfo[run].glyphCount = 0;
393            fStyleRunInfo[run].glyphs = NULL;
394            fStyleRunInfo[run].positions = NULL;
395        }
396    }
397
398    fGlyphCount = 0;
399    for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
400        fStyleRunInfo[run].font      = fFontRuns->getFont(styleIndices[0]);
401        fStyleRunInfo[run].runBase   = runStart;
402        fStyleRunInfo[run].runLimit  = fStyleRunLimits[run];
403        fStyleRunInfo[run].script    = (UScriptCode) fScriptRuns->getValue(styleIndices[2]);
404        fStyleRunInfo[run].locale    = fLocaleRuns->getLocale(styleIndices[3]);
405        fStyleRunInfo[run].level     = (UBiDiLevel) fLevelRuns->getValue(styleIndices[1]);
406        fStyleRunInfo[run].glyphBase = fGlyphCount;
407
408        fStyleRunInfo[run].engine = LayoutEngine::layoutEngineFactory(fStyleRunInfo[run].font,
409            fStyleRunInfo[run].script, getLanguageCode(fStyleRunInfo[run].locale), layoutStatus);
410        if (LE_FAILURE(layoutStatus)) {
411            status = layoutStatus;
412            return;
413        }
414
415        fStyleRunInfo[run].glyphCount = fStyleRunInfo[run].engine->layoutChars(fChars, runStart, fStyleRunLimits[run] - runStart, fCharCount,
416            fStyleRunInfo[run].level & 1, 0, 0, layoutStatus);
417        if (LE_FAILURE(layoutStatus)) {
418            status = layoutStatus;
419            return;
420        }
421
422        runStart = fStyleRunLimits[run];
423        styleIndices += styleCount;
424        fGlyphCount += fStyleRunInfo[run].glyphCount;
425    }
426
427    // Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps,
428    // in logical order. (Both maps need an extra entry for the end of the text.)
429    //
430    // For each layout get the positions and convert them into glyph widths, in
431    // logical order. Get the glyph-to-char mapping, offset by starting index in the
432    // character array. Swap the glyph width and glyph-to-char arrays into logical order.
433    // Finally, fill in the char-to-glyph mappings.
434    fGlyphWidths       = LE_NEW_ARRAY(float, fGlyphCount);
435    fGlyphToCharMap    = LE_NEW_ARRAY(le_int32, fGlyphCount + 1);
436    fCharToMinGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
437    fCharToMaxGlyphMap = LE_NEW_ARRAY(le_int32, fCharCount + 1);
438    if ((fGlyphWidths == NULL) || (fGlyphToCharMap == NULL) ||
439        (fCharToMinGlyphMap == NULL) || (fCharToMaxGlyphMap == NULL)) {
440        status = LE_MEMORY_ALLOCATION_ERROR;
441        return;
442    }
443
444    le_int32 glyph;
445
446    for (runStart = 0, run = 0; run < fStyleRunCount; run += 1) {
447        LayoutEngine *engine = fStyleRunInfo[run].engine;
448        le_int32 glyphCount  = fStyleRunInfo[run].glyphCount;
449        le_int32 glyphBase   = fStyleRunInfo[run].glyphBase;
450
451        fStyleRunInfo[run].glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount);
452        fStyleRunInfo[run].positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2);
453        if ((fStyleRunInfo[run].glyphs == NULL) ||
454            (fStyleRunInfo[run].positions == NULL)) {
455            status = LE_MEMORY_ALLOCATION_ERROR;
456            return;
457        }
458
459        engine->getGlyphs(fStyleRunInfo[run].glyphs, layoutStatus);
460        if (LE_FAILURE(layoutStatus)) {
461            status = layoutStatus;
462            return;
463        }
464
465        engine->getGlyphPositions(fStyleRunInfo[run].positions, layoutStatus);
466        if (LE_FAILURE(layoutStatus)) {
467            status = layoutStatus;
468            return;
469        }
470
471        engine->getCharIndices(&fGlyphToCharMap[glyphBase], runStart, layoutStatus);
472        if (LE_FAILURE(layoutStatus)) {
473            status = layoutStatus;
474            return;
475        }
476
477        for (glyph = 0; glyph < glyphCount; glyph += 1) {
478            fGlyphWidths[glyphBase + glyph] = fStyleRunInfo[run].positions[glyph * 2 + 2] - fStyleRunInfo[run].positions[glyph * 2];
479        }
480
481        if ((fStyleRunInfo[run].level & 1) != 0) {
482            LXUtilities::reverse(&fGlyphWidths[glyphBase], glyphCount);
483            LXUtilities::reverse(&fGlyphToCharMap[glyphBase], glyphCount);
484        }
485
486        runStart = fStyleRunLimits[run];
487
488        delete engine;
489        fStyleRunInfo[run].engine = NULL;
490    }
491
492    fGlyphToCharMap[fGlyphCount] = fCharCount;
493
494    // Initialize the char-to-glyph maps to -1 so that we can later figure out
495    // whether any of the entries in the map aren't filled in below.
496    le_int32 chIndex;
497    for (chIndex = 0; chIndex <= fCharCount; chIndex += 1) {
498        fCharToMinGlyphMap[chIndex] = -1;
499        fCharToMaxGlyphMap[chIndex] = -1;
500    }
501
502    for (glyph = fGlyphCount - 1; glyph >= 0; glyph -= 1) {
503        le_int32 ch = fGlyphToCharMap[glyph];
504
505        fCharToMinGlyphMap[ch] = glyph;
506    }
507
508    fCharToMinGlyphMap[fCharCount] = fGlyphCount;
509
510    for (glyph = 0; glyph < fGlyphCount; glyph += 1) {
511        le_int32 ch = fGlyphToCharMap[glyph];
512
513        fCharToMaxGlyphMap[ch] = glyph;
514    }
515
516    fCharToMaxGlyphMap[fCharCount] = fGlyphCount;
517
518    // Now fill in the missing values in the char-to-glyph maps.
519    fillMissingCharToGlyphMapValues(fCharToMinGlyphMap, fCharCount);
520    fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap, fCharCount);
521}
522
523ParagraphLayout::~ParagraphLayout()
524{
525    delete (FontRuns *) fFontRuns;
526
527    if (! fClientLevels) {
528        delete (ValueRuns *) fLevelRuns;
529        fLevelRuns = NULL;
530
531        fClientLevels = TRUE;
532    }
533
534    if (! fClientScripts) {
535        delete (ValueRuns *) fScriptRuns;
536        fScriptRuns = NULL;
537
538        fClientScripts = TRUE;
539    }
540
541    if (! fClientLocales) {
542        delete (LocaleRuns *) fLocaleRuns;
543        fLocaleRuns = NULL;
544
545        fClientLocales = TRUE;
546    }
547
548    if (fEmbeddingLevels != NULL) {
549        LE_DELETE_ARRAY(fEmbeddingLevels);
550        fEmbeddingLevels = NULL;
551    }
552
553    if (fGlyphToCharMap != NULL) {
554        LE_DELETE_ARRAY(fGlyphToCharMap);
555        fGlyphToCharMap = NULL;
556    }
557
558    if (fCharToMinGlyphMap != NULL) {
559        LE_DELETE_ARRAY(fCharToMinGlyphMap);
560        fCharToMinGlyphMap = NULL;
561    }
562
563    if (fCharToMaxGlyphMap != NULL) {
564        LE_DELETE_ARRAY(fCharToMaxGlyphMap);
565        fCharToMaxGlyphMap = NULL;
566    }
567
568    if (fGlyphWidths != NULL) {
569        LE_DELETE_ARRAY(fGlyphWidths);
570        fGlyphWidths = NULL;
571    }
572
573    if (fParaBidi != NULL) {
574        ubidi_close(fParaBidi);
575        fParaBidi = NULL;
576    }
577
578    if (fLineBidi != NULL) {
579        ubidi_close(fLineBidi);
580        fLineBidi = NULL;
581    }
582
583    if (fStyleRunCount > 0) {
584        le_int32 run;
585
586        LE_DELETE_ARRAY(fStyleRunLimits);
587        LE_DELETE_ARRAY(fStyleIndices);
588
589        for (run = 0; run < fStyleRunCount; run += 1) {
590            LE_DELETE_ARRAY(fStyleRunInfo[run].glyphs);
591            LE_DELETE_ARRAY(fStyleRunInfo[run].positions);
592
593            fStyleRunInfo[run].glyphs    = NULL;
594            fStyleRunInfo[run].positions = NULL;
595        }
596
597        LE_DELETE_ARRAY(fStyleRunInfo);
598
599        fStyleRunLimits = NULL;
600        fStyleIndices   = NULL;
601        fStyleRunInfo        = NULL;
602        fStyleRunCount  = 0;
603    }
604
605    if (fBreakIterator != NULL) {
606        delete fBreakIterator;
607        fBreakIterator = NULL;
608    }
609}
610
611
612le_bool ParagraphLayout::isComplex(const LEUnicode chars[], le_int32 count)
613{
614    UErrorCode scriptStatus = U_ZERO_ERROR;
615    UScriptCode scriptCode  = USCRIPT_INVALID_CODE;
616    UScriptRun *sr = uscript_openRun(chars, count, &scriptStatus);
617    le_bool result = FALSE;
618
619    while (uscript_nextRun(sr, NULL, NULL, &scriptCode)) {
620        if (isComplex(scriptCode)) {
621            result = TRUE;
622            break;
623        }
624    }
625
626    uscript_closeRun(sr);
627    return result;
628}
629
630le_int32 ParagraphLayout::getAscent() const
631{
632    if (fAscent <= 0 && fCharCount > 0) {
633        ((ParagraphLayout *) this)->computeMetrics();
634    }
635
636    return fAscent;
637}
638
639le_int32 ParagraphLayout::getDescent() const
640{
641    if (fAscent <= 0 && fCharCount > 0) {
642        ((ParagraphLayout *) this)->computeMetrics();
643    }
644
645    return fDescent;
646}
647
648le_int32 ParagraphLayout::getLeading() const
649{
650    if (fAscent <= 0 && fCharCount > 0) {
651        ((ParagraphLayout *) this)->computeMetrics();
652    }
653
654    return fLeading;
655}
656
657le_bool ParagraphLayout::isDone() const
658{
659    return fLineEnd >= fCharCount;
660}
661
662ParagraphLayout::Line *ParagraphLayout::nextLine(float width)
663{
664    if (isDone()) {
665        return NULL;
666    }
667
668    fLineStart = fLineEnd;
669
670    if (width > 0) {
671        le_int32 glyph    = fCharToMinGlyphMap[fLineStart];
672        float widthSoFar  = 0;
673
674        while (glyph < fGlyphCount && widthSoFar + fGlyphWidths[glyph] <= width) {
675            widthSoFar += fGlyphWidths[glyph++];
676        }
677
678        // If no glyphs fit on the line, force one to fit.
679        //
680        // (There shouldn't be any zero width glyphs at the
681        // start of a line unless the paragraph consists of
682        // only zero width glyphs, because otherwise the zero
683        // width glyphs will have been included on the end of
684        // the previous line...)
685        if (widthSoFar == 0 && glyph < fGlyphCount) {
686            glyph += 1;
687        }
688
689        fLineEnd = previousBreak(fGlyphToCharMap[glyph]);
690
691        // If this break is at or before the last one,
692        // find a glyph, starting at the one which didn't
693        // fit, that produces a break after the last one.
694        while (fLineEnd <= fLineStart) {
695            fLineEnd = fGlyphToCharMap[glyph++];
696        }
697    } else {
698        fLineEnd = fCharCount;
699    }
700
701    return computeVisualRuns();
702}
703
704void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel)
705{
706    UErrorCode bidiStatus = U_ZERO_ERROR;
707
708    if (fLevelRuns != NULL) {
709        le_int32 ch;
710        le_int32 run;
711
712        fEmbeddingLevels = LE_NEW_ARRAY(UBiDiLevel, fCharCount);
713
714        for (ch = 0, run = 0; run < fLevelRuns->getCount(); run += 1) {
715            UBiDiLevel runLevel = (UBiDiLevel) fLevelRuns->getValue(run) | UBIDI_LEVEL_OVERRIDE;
716            le_int32   runLimit = fLevelRuns->getLimit(run);
717
718            while (ch < runLimit) {
719                fEmbeddingLevels[ch++] = runLevel;
720            }
721        }
722    }
723
724    fParaBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
725    ubidi_setPara(fParaBidi, fChars, fCharCount, paragraphLevel, fEmbeddingLevels, &bidiStatus);
726
727    if (fLevelRuns == NULL) {
728        le_int32 levelRunCount = ubidi_countRuns(fParaBidi, &bidiStatus);
729        ValueRuns *levelRuns = new ValueRuns(levelRunCount);
730
731        le_int32 logicalStart = 0;
732        le_int32 run;
733        le_int32 limit;
734        UBiDiLevel level;
735
736        for (run = 0; run < levelRunCount; run += 1) {
737            ubidi_getLogicalRun(fParaBidi, logicalStart, &limit, &level);
738            levelRuns->add(level, limit);
739            logicalStart = limit;
740        }
741
742        fLevelRuns    = levelRuns;
743        fClientLevels = FALSE;
744    }
745}
746
747void ParagraphLayout::computeScripts()
748{
749    UErrorCode scriptStatus = U_ZERO_ERROR;
750    UScriptRun *sr = uscript_openRun(fChars, fCharCount, &scriptStatus);
751    ValueRuns  *scriptRuns = new ValueRuns(0);
752    le_int32 limit;
753    UScriptCode script;
754
755    while (uscript_nextRun(sr, NULL, &limit, &script)) {
756        scriptRuns->add(script, limit);
757    }
758
759    uscript_closeRun(sr);
760
761    fScriptRuns    = scriptRuns;
762    fClientScripts = FALSE;
763}
764
765void ParagraphLayout::computeLocales()
766{
767    LocaleRuns *localeRuns = new LocaleRuns(0);
768    const Locale *defaultLocale = &Locale::getDefault();
769
770    localeRuns->add(defaultLocale, fCharCount);
771
772    fLocaleRuns    = localeRuns;
773    fClientLocales = FALSE;
774}
775
776void ParagraphLayout::computeSubFonts(const FontRuns *fontRuns, LEErrorCode &status)
777{
778    if (LE_FAILURE(status)) {
779        return;
780    }
781
782    const RunArray *styleRunArrays[] = {fontRuns, fScriptRuns};
783    le_int32 styleCount = sizeof styleRunArrays / sizeof styleRunArrays[0];
784    StyleRuns styleRuns(styleRunArrays, styleCount);
785    le_int32 styleRunCount = styleRuns.getRuns(NULL, NULL);
786    le_int32 *styleRunLimits = LE_NEW_ARRAY(le_int32, styleRunCount);
787    le_int32 *styleIndices = LE_NEW_ARRAY(le_int32, styleRunCount * styleCount);
788    FontRuns *subFontRuns  = new FontRuns(0);
789    le_int32  run, offset, *si;
790
791    styleRuns.getRuns(styleRunLimits, styleIndices);
792
793    si = styleIndices;
794    offset = 0;
795
796    for (run = 0; run < styleRunCount; run += 1) {
797        const LEFontInstance *runFont = fontRuns->getFont(si[0]);
798        le_int32 script = fScriptRuns->getValue(si[1]);
799
800        while (offset < styleRunLimits[run]) {
801            const LEFontInstance *subFont = runFont->getSubFont(fChars, &offset, styleRunLimits[run], script, status);
802
803            if (LE_FAILURE(status)) {
804                delete subFontRuns;
805                goto cleanUp;
806            }
807
808            subFontRuns->add(subFont, offset);
809        }
810
811        si += styleCount;
812    }
813
814    fFontRuns = subFontRuns;
815
816cleanUp:
817    LE_DELETE_ARRAY(styleIndices);
818    LE_DELETE_ARRAY(styleRunLimits);
819}
820
821void ParagraphLayout::computeMetrics()
822{
823    le_int32 i, count = fFontRuns->getCount();
824    le_int32 maxDL = 0;
825
826    for (i = 0; i < count; i += 1) {
827        const LEFontInstance *font = fFontRuns->getFont(i);
828        le_int32 ascent  = font->getAscent();
829        le_int32 descent = font->getDescent();
830        le_int32 leading = font->getLeading();
831        le_int32 dl      = descent + leading;
832
833        if (ascent > fAscent) {
834            fAscent = ascent;
835        }
836
837        if (descent > fDescent) {
838            fDescent = descent;
839        }
840
841        if (leading > fLeading) {
842            fLeading = leading;
843        }
844
845        if (dl > maxDL) {
846            maxDL = dl;
847        }
848    }
849
850    fLeading = maxDL - fDescent;
851}
852
853#if 1
854struct LanguageMap
855{
856    const char *localeCode;
857    le_int32 languageCode;
858};
859
860static const LanguageMap languageMap[] =
861{
862    {"afr", afkLanguageCode}, // Afrikaans
863    {"ara", araLanguageCode}, // Arabic
864    {"asm", asmLanguageCode}, // Assamese
865    {"bel", belLanguageCode}, // Belarussian
866    {"ben", benLanguageCode}, // Bengali
867    {"bod", tibLanguageCode}, // Tibetan
868    {"bul", bgrLanguageCode}, // Bulgarian
869    {"cat", catLanguageCode}, // Catalan
870    {"ces", csyLanguageCode}, // Czech
871    {"che", cheLanguageCode}, // Chechen
872    {"cop", copLanguageCode}, // Coptic
873    {"cym", welLanguageCode}, // Welsh
874    {"dan", danLanguageCode}, // Danish
875    {"deu", deuLanguageCode}, // German
876    {"dzo", dznLanguageCode}, // Dzongkha
877    {"ell", ellLanguageCode}, // Greek
878    {"eng", engLanguageCode}, // English
879    {"est", etiLanguageCode}, // Estonian
880    {"eus", euqLanguageCode}, // Basque
881    {"fas", farLanguageCode}, // Farsi
882    {"fin", finLanguageCode}, // Finnish
883    {"fra", fraLanguageCode}, // French
884    {"gle", gaeLanguageCode}, // Irish Gaelic
885    {"guj", gujLanguageCode}, // Gujarati
886    {"hau", hauLanguageCode}, // Hausa
887    {"heb", iwrLanguageCode}, // Hebrew
888    {"hin", hinLanguageCode}, // Hindi
889    {"hrv", hrvLanguageCode}, // Croatian
890    {"hun", hunLanguageCode}, // Hungarian
891    {"hye", hyeLanguageCode}, // Armenian
892    {"ind", indLanguageCode}, // Indonesian
893    {"ita", itaLanguageCode}, // Italian
894    {"jpn", janLanguageCode}, // Japanese
895    {"kan", kanLanguageCode}, // Kannada
896    {"kas", kshLanguageCode}, // Kashmiri
897    {"khm", khmLanguageCode}, // Khmer
898    {"kok", kokLanguageCode}, // Konkani
899    {"kor", korLanguageCode}, // Korean
900//  {"mal_XXX", malLanguageCode}, // Malayalam - Traditional
901    {"mal", mlrLanguageCode}, // Malayalam - Reformed
902    {"mar", marLanguageCode}, // Marathi
903    {"mlt", mtsLanguageCode}, // Maltese
904    {"mni", mniLanguageCode}, // Manipuri
905    {"mon", mngLanguageCode}, // Mongolian
906    {"nep", nepLanguageCode}, // Nepali
907    {"ori", oriLanguageCode}, // Oriya
908    {"pol", plkLanguageCode}, // Polish
909    {"por", ptgLanguageCode}, // Portuguese
910    {"pus", pasLanguageCode}, // Pashto
911    {"ron", romLanguageCode}, // Romanian
912    {"rus", rusLanguageCode}, // Russian
913    {"san", sanLanguageCode}, // Sanskrit
914    {"sin", snhLanguageCode}, // Sinhalese
915    {"slk", skyLanguageCode}, // Slovak
916    {"snd", sndLanguageCode}, // Sindhi
917    {"slv", slvLanguageCode}, // Slovenian
918    {"spa", espLanguageCode}, // Spanish
919    {"sqi", sqiLanguageCode}, // Albanian
920    {"srp", srbLanguageCode}, // Serbian
921    {"swe", sveLanguageCode}, // Swedish
922    {"syr", syrLanguageCode}, // Syriac
923    {"tam", tamLanguageCode}, // Tamil
924    {"tel", telLanguageCode}, // Telugu
925    {"tha", thaLanguageCode}, // Thai
926    {"tur", trkLanguageCode}, // Turkish
927    {"urd", urdLanguageCode}, // Urdu
928    {"yid", jiiLanguageCode}, // Yiddish
929//  {"zhp", zhpLanguageCode}, // Chinese - Phonetic
930    {"zho", zhsLanguageCode}, // Chinese
931    {"zho_CHN", zhsLanguageCode}, // Chinese - China
932    {"zho_HKG", zhsLanguageCode}, // Chinese - Hong Kong
933    {"zho_MAC", zhtLanguageCode}, // Chinese - Macao
934    {"zho_SGP", zhsLanguageCode}, // Chinese - Singapore
935    {"zho_TWN", zhtLanguageCode}  // Chinese - Taiwan
936};
937
938static const le_int32 languageMapCount = ARRAY_SIZE(languageMap);
939
940le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
941{
942    char code[8] = {0, 0, 0, 0, 0, 0, 0, 0};
943    const char *language = locale->getISO3Language();
944    const char *country  = locale->getISO3Country();
945
946    uprv_strcat(code, language);
947
948    if ((uprv_strcmp(language, "zho") == 0) && country != NULL) {
949        uprv_strcat(code, "_");
950        uprv_strcat(code, country);
951    }
952
953    for (le_int32 i = 0; i < languageMapCount; i += 1) {
954        if (uprv_strcmp(code, languageMap[i].localeCode) == 0) {
955            return languageMap[i].languageCode;
956        }
957    }
958
959    return nullLanguageCode;
960}
961#else
962
963// TODO - dummy implementation for right now...
964le_int32 ParagraphLayout::getLanguageCode(const Locale *locale)
965{
966    return nullLanguageCode;
967}
968#endif
969
970le_bool ParagraphLayout::isComplex(UScriptCode script)
971{
972    if (script < 0 || script >= (UScriptCode) scriptCodeCount) {
973        return FALSE;
974    }
975
976    return complexTable[script];
977}
978
979le_int32 ParagraphLayout::previousBreak(le_int32 charIndex)
980{
981    // skip over any whitespace or control characters,
982    // because they can hang in the margin.
983    while (charIndex < fCharCount &&
984           (u_isWhitespace(fChars[charIndex]) ||
985            u_iscntrl(fChars[charIndex]))) {
986        charIndex += 1;
987    }
988
989    // Create the BreakIterator if we don't already have one
990    if (fBreakIterator == NULL) {
991        Locale thai("th");
992        UCharCharacterIterator *iter = new UCharCharacterIterator(fChars, fCharCount);
993        UErrorCode status = U_ZERO_ERROR;
994
995        fBreakIterator = BreakIterator::createLineInstance(thai, status);
996        fBreakIterator->adoptText(iter);
997    }
998
999    // return the break location that's at or before
1000    // the character we stopped on. Note: if we're
1001    // on a break, the "+ 1" will cause preceding to
1002    // back up to it.
1003    return fBreakIterator->preceding(charIndex + 1);
1004}
1005
1006ParagraphLayout::Line *ParagraphLayout::computeVisualRuns()
1007{
1008    UErrorCode bidiStatus = U_ZERO_ERROR;
1009    le_int32 dirRunCount, visualRun;
1010
1011    fVisualRunLastX = 0;
1012    fVisualRunLastY = 0;
1013    fFirstVisualRun = getCharRun(fLineStart);
1014    fLastVisualRun  = getCharRun(fLineEnd - 1);
1015
1016    if (fLineBidi == NULL) {
1017        fLineBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
1018    }
1019
1020    ubidi_setLine(fParaBidi, fLineStart, fLineEnd, fLineBidi, &bidiStatus);
1021    dirRunCount = ubidi_countRuns(fLineBidi, &bidiStatus);
1022
1023    Line *line = new Line();
1024
1025    for (visualRun = 0; visualRun < dirRunCount; visualRun += 1) {
1026        le_int32 relStart, run, runLength;
1027        UBiDiDirection runDirection = ubidi_getVisualRun(fLineBidi, visualRun, &relStart, &runLength);
1028        le_int32 runStart = fLineStart + relStart;
1029        le_int32 runEnd   = runStart + runLength - 1;
1030        le_int32 firstRun = getCharRun(runStart);
1031        le_int32 lastRun  = getCharRun(runEnd);
1032        le_int32 startRun = (runDirection == UBIDI_LTR)? firstRun : lastRun;
1033        le_int32 stopRun  = (runDirection == UBIDI_LTR)? lastRun + 1 : firstRun - 1;
1034        le_int32 dir      = (runDirection == UBIDI_LTR)?  1 : -1;
1035
1036        for (run = startRun; run != stopRun; run += dir) {
1037            le_int32 firstChar = (run == firstRun)? runStart : fStyleRunInfo[run].runBase;
1038            le_int32 lastChar  = (run == lastRun)?  runEnd   : fStyleRunInfo[run].runLimit - 1;
1039
1040            appendRun(line, run, firstChar, lastChar);
1041        }
1042    }
1043
1044    return line;
1045}
1046
1047void ParagraphLayout::appendRun(ParagraphLayout::Line *line, le_int32 run, le_int32 firstChar, le_int32 lastChar)
1048{
1049    le_int32 glyphBase = fStyleRunInfo[run].glyphBase;
1050    le_int32 inGlyph, outGlyph;
1051
1052    // Get the glyph indices for all the characters between firstChar and lastChar,
1053    // make the minimum one be leftGlyph and the maximum one be rightGlyph.
1054    // (need to do this to handle local reorderings like Indic left matras)
1055    le_int32 leftGlyph  = fGlyphCount;
1056    le_int32 rightGlyph = -1;
1057    le_int32 ch;
1058
1059    for (ch = firstChar; ch <= lastChar; ch += 1) {
1060        le_int32 minGlyph = fCharToMinGlyphMap[ch];
1061        le_int32 maxGlyph = fCharToMaxGlyphMap[ch];
1062
1063        if (minGlyph < leftGlyph) {
1064            leftGlyph = minGlyph;
1065        }
1066
1067        if (maxGlyph > rightGlyph) {
1068            rightGlyph = maxGlyph;
1069        }
1070    }
1071
1072    if ((fStyleRunInfo[run].level & 1) != 0) {
1073        le_int32 swap = rightGlyph;
1074        le_int32 last = glyphBase + fStyleRunInfo[run].glyphCount - 1;
1075
1076        // Here, we want to remove the glyphBase bias...
1077        rightGlyph = last - leftGlyph;
1078        leftGlyph  = last - swap;
1079    } else {
1080        rightGlyph -= glyphBase;
1081        leftGlyph  -= glyphBase;
1082    }
1083
1084    // Set the position bias for the glyphs. If we're at the start of
1085    // a line, we want the first glyph to be at x = 0, even if it comes
1086    // from the middle of a layout. If we've got a right-to-left run, we
1087    // want the left-most glyph to start at the final x position of the
1088    // previous run, even though this glyph may be in the middle of the
1089    // run.
1090    fVisualRunLastX -= fStyleRunInfo[run].positions[leftGlyph * 2];
1091
1092    // Make rightGlyph be the glyph just to the right of
1093    // the run's glyphs
1094    rightGlyph += 1;
1095
1096    UBiDiDirection direction  = ((fStyleRunInfo[run].level & 1) == 0)? UBIDI_LTR : UBIDI_RTL;
1097    le_int32   glyphCount     = rightGlyph - leftGlyph;
1098    LEGlyphID *glyphs         = LE_NEW_ARRAY(LEGlyphID, glyphCount);
1099    float     *positions      = LE_NEW_ARRAY(float, glyphCount * 2 + 2);
1100    le_int32  *glyphToCharMap = LE_NEW_ARRAY(le_int32, glyphCount);
1101
1102    LE_ARRAY_COPY(glyphs, &fStyleRunInfo[run].glyphs[leftGlyph], glyphCount);
1103
1104    for (outGlyph = 0, inGlyph = leftGlyph * 2; inGlyph <= rightGlyph * 2; inGlyph += 2, outGlyph += 2) {
1105        positions[outGlyph]     = fStyleRunInfo[run].positions[inGlyph] + fVisualRunLastX;
1106        positions[outGlyph + 1] = fStyleRunInfo[run].positions[inGlyph + 1] + fVisualRunLastY;
1107    }
1108
1109    // Save the ending position of this run
1110    // to use for the start of the next run
1111    fVisualRunLastX = positions[outGlyph - 2];
1112    fVisualRunLastY = positions[outGlyph - 1];
1113
1114    if ((fStyleRunInfo[run].level & 1) == 0) {
1115        for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) {
1116            glyphToCharMap[outGlyph] = fGlyphToCharMap[glyphBase + inGlyph];
1117        }
1118    } else {
1119        // Because fGlyphToCharMap is stored in logical order to facilitate line breaking,
1120        // we need to map the physical glyph indices to logical indices while we copy the
1121        // character indices.
1122        le_int32 base = glyphBase + fStyleRunInfo[run].glyphCount - 1;
1123
1124        for (outGlyph = 0, inGlyph = leftGlyph; inGlyph < rightGlyph; inGlyph += 1, outGlyph += 1) {
1125            glyphToCharMap[outGlyph] = fGlyphToCharMap[base - inGlyph];
1126        }
1127    }
1128
1129    line->append(fStyleRunInfo[run].font, direction, glyphCount, glyphs, positions, glyphToCharMap);
1130}
1131
1132le_int32 ParagraphLayout::getCharRun(le_int32 charIndex)
1133{
1134    if (charIndex < 0 || charIndex > fCharCount) {
1135        return -1;
1136    }
1137
1138    le_int32 run;
1139
1140    // NOTE: as long as fStyleRunLimits is well-formed
1141    // the above range check guarantees that we'll never
1142    // fall off the end of the array.
1143    run = 0;
1144    while (charIndex >= fStyleRunLimits[run]) {
1145        run += 1;
1146    }
1147
1148    return run;
1149}
1150
1151
1152const char ParagraphLayout::Line::fgClassID = 0;
1153
1154#define INITIAL_RUN_CAPACITY 4
1155#define RUN_CAPACITY_GROW_LIMIT 16
1156
1157ParagraphLayout::Line::~Line()
1158{
1159    le_int32 i;
1160
1161    for (i = 0; i < fRunCount; i += 1) {
1162        delete fRuns[i];
1163    }
1164
1165    LE_DELETE_ARRAY(fRuns);
1166}
1167
1168le_int32 ParagraphLayout::Line::getAscent() const
1169{
1170    if (fAscent <= 0) {
1171        ((ParagraphLayout::Line *)this)->computeMetrics();
1172    }
1173
1174    return fAscent;
1175}
1176
1177le_int32 ParagraphLayout::Line::getDescent() const
1178{
1179    if (fAscent <= 0) {
1180        ((ParagraphLayout::Line *)this)->computeMetrics();
1181    }
1182
1183    return fDescent;
1184}
1185
1186le_int32 ParagraphLayout::Line::getLeading() const
1187{
1188    if (fAscent <= 0) {
1189        ((ParagraphLayout::Line *)this)->computeMetrics();
1190    }
1191
1192    return fLeading;
1193}
1194
1195le_int32 ParagraphLayout::Line::getWidth() const
1196{
1197    const VisualRun *lastRun = getVisualRun(fRunCount - 1);
1198
1199    if (lastRun == NULL) {
1200        return 0;
1201    }
1202
1203    le_int32 glyphCount = lastRun->getGlyphCount();
1204    const float *positions = lastRun->getPositions();
1205
1206    return (le_int32) positions[glyphCount * 2];
1207}
1208
1209const ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(le_int32 runIndex) const
1210{
1211    if (runIndex < 0 || runIndex >= fRunCount) {
1212        return NULL;
1213    }
1214
1215    return fRuns[runIndex];
1216}
1217
1218void ParagraphLayout::Line::append(const LEFontInstance *font, UBiDiDirection direction, le_int32 glyphCount,
1219                                   const LEGlyphID glyphs[], const float positions[], const le_int32 glyphToCharMap[])
1220{
1221    if (fRunCount >= fRunCapacity) {
1222        if (fRunCapacity == 0) {
1223            fRunCapacity = INITIAL_RUN_CAPACITY;
1224            fRuns = LE_NEW_ARRAY(ParagraphLayout::VisualRun *, fRunCapacity);
1225        } else {
1226            fRunCapacity += (fRunCapacity < RUN_CAPACITY_GROW_LIMIT? fRunCapacity : RUN_CAPACITY_GROW_LIMIT);
1227            fRuns = (ParagraphLayout::VisualRun **) LE_GROW_ARRAY(fRuns, fRunCapacity);
1228        }
1229    }
1230
1231    fRuns[fRunCount++] = new ParagraphLayout::VisualRun(font, direction, glyphCount, glyphs, positions, glyphToCharMap);
1232}
1233
1234void ParagraphLayout::Line::computeMetrics()
1235{
1236    le_int32 maxDL = 0;
1237
1238    for (le_int32 i = 0; i < fRunCount; i += 1) {
1239        le_int32 ascent  = fRuns[i]->getAscent();
1240        le_int32 descent = fRuns[i]->getDescent();
1241        le_int32 leading = fRuns[i]->getLeading();
1242        le_int32 dl      = descent + leading;
1243
1244        if (ascent > fAscent) {
1245            fAscent = ascent;
1246        }
1247
1248        if (descent > fDescent) {
1249            fDescent = descent;
1250        }
1251
1252        if (leading > fLeading) {
1253            fLeading = leading;
1254        }
1255
1256        if (dl > maxDL) {
1257            maxDL = dl;
1258        }
1259    }
1260
1261    fLeading = maxDL - fDescent;
1262}
1263
1264const char ParagraphLayout::VisualRun::fgClassID = 0;
1265
1266ParagraphLayout::VisualRun::~VisualRun()
1267{
1268    LE_DELETE_ARRAY(fGlyphToCharMap);
1269    LE_DELETE_ARRAY(fPositions);
1270    LE_DELETE_ARRAY(fGlyphs);
1271}
1272
1273U_NAMESPACE_END
1274
1275#endif
1276
1277