]> git.saurik.com Git - apple/icu.git/blame - icuSources/layoutex/ParagraphLayout.cpp
ICU-57166.0.1.tar.gz
[apple/icu.git] / icuSources / layoutex / ParagraphLayout.cpp
CommitLineData
b75a7d8f
A
1/*
2 **********************************************************************
57a6839d 3 * Copyright (C) 2002-2014, International Business Machines
b75a7d8f
A
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"
73c04bcf 13#include "layout/LEScripts.h"
b75a7d8f
A
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
46f4442e
A
33/* Leave this copyright notice here! It needs to go somewhere in this library. */
34static const char copyright[] = U_COPYRIGHT_STRING;
35
b75a7d8f
A
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/*
374ca955 133 * NOTE: This table only has "TRUE" values for
b75a7d8f
A
134 * those scripts which the LayoutEngine can currently
135 * process, rather for all scripts which require
136 * complex processing for correct rendering.
137 */
73c04bcf 138static const le_bool complexTable[scriptCodeCount] = {
374ca955
A
139 FALSE , /* Zyyy */
140 FALSE, /* Qaai */
141 TRUE, /* Arab */
142 FALSE, /* Armn */
143 TRUE, /* Beng */
144 FALSE, /* Bopo */
145 FALSE, /* Cher */
73c04bcf 146 FALSE, /* Copt=Qaac */
374ca955
A
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 */
73c04bcf
A
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 */
46f4442e
A
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 */
729e4ab9
A
268 FALSE, /* Zsym */
269 FALSE, /* Bamu */
270 FALSE, /* Lisu */
271 FALSE, /* Nkgb */
272 FALSE /* Sarb */
b75a7d8f
A
273};
274
275
276const char ParagraphLayout::fgClassID = 0;
277
729e4ab9
A
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
b75a7d8f
A
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,
374ca955
A
316 UBiDiLevel paragraphLevel, le_bool vertical,
317 LEErrorCode &status)
b75a7d8f
A
318 : fChars(chars), fCharCount(count),
319 fFontRuns(NULL), fLevelRuns(levelRuns), fScriptRuns(scriptRuns), fLocaleRuns(localeRuns),
374ca955 320 fVertical(vertical), fClientLevels(TRUE), fClientScripts(TRUE), fClientLocales(TRUE), fEmbeddingLevels(NULL),
b75a7d8f 321 fAscent(0), fDescent(0), fLeading(0),
374ca955 322 fGlyphToCharMap(NULL), fCharToMinGlyphMap(NULL), fCharToMaxGlyphMap(NULL), fGlyphWidths(NULL), fGlyphCount(0),
b75a7d8f
A
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{
374ca955
A
329
330 if (LE_FAILURE(status)) {
331 fCharCount = -1;
332 return;
333 }
334
57a6839d
A
335 (void)copyright; // Suppress unused variable warning.
336 (void)fVertical; // Suppress warning for unused field fVertical.
337
b75a7d8f
A
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
374ca955
A
350 computeSubFonts(fontRuns, status);
351
352 if (LE_FAILURE(status)) {
353 //other stuff?
354 fCharCount = -1;
355 return;
356 }
b75a7d8f
A
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;
57a6839d 363
b75a7d8f
A
364 fStyleRunCount = styleRuns.getRuns(NULL, NULL);
365
366 fStyleRunLimits = LE_NEW_ARRAY(le_int32, fStyleRunCount);
367 fStyleIndices = LE_NEW_ARRAY(le_int32, fStyleRunCount * styleCount);
729e4ab9
A
368 if ((fStyleRunLimits == NULL) || (fStyleIndices == NULL)) {
369 status = LE_MEMORY_ALLOCATION_ERROR;
370 return;
371 }
372
b75a7d8f
A
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);
729e4ab9
A
380 if (fStyleRunInfo == NULL) {
381 status = LE_MEMORY_ALLOCATION_ERROR;
382 return;
383 }
384 else {
385 // initialize
b331163b 386 for (run = 0; run < fStyleRunCount; run += 1) {
729e4ab9
A
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 }
b75a7d8f
A
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);
729e4ab9
A
413 if (LE_FAILURE(layoutStatus)) {
414 status = layoutStatus;
415 return;
416 }
b75a7d8f
A
417
418 fStyleRunInfo[run].glyphCount = fStyleRunInfo[run].engine->layoutChars(fChars, runStart, fStyleRunLimits[run] - runStart, fCharCount,
419 fStyleRunInfo[run].level & 1, 0, 0, layoutStatus);
729e4ab9
A
420 if (LE_FAILURE(layoutStatus)) {
421 status = layoutStatus;
422 return;
423 }
b75a7d8f
A
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,
57a6839d 431 // in logical order. (Both maps need an extra entry for the end of the text.)
b75a7d8f
A
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
374ca955
A
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);
57a6839d 441 if ((fGlyphWidths == NULL) || (fGlyphToCharMap == NULL) ||
729e4ab9
A
442 (fCharToMinGlyphMap == NULL) || (fCharToMaxGlyphMap == NULL)) {
443 status = LE_MEMORY_ALLOCATION_ERROR;
444 return;
445 }
374ca955
A
446
447 le_int32 glyph;
b75a7d8f
A
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;
b75a7d8f
A
453
454 fStyleRunInfo[run].glyphs = LE_NEW_ARRAY(LEGlyphID, glyphCount);
455 fStyleRunInfo[run].positions = LE_NEW_ARRAY(float, glyphCount * 2 + 2);
57a6839d 456 if ((fStyleRunInfo[run].glyphs == NULL) ||
729e4ab9
A
457 (fStyleRunInfo[run].positions == NULL)) {
458 status = LE_MEMORY_ALLOCATION_ERROR;
459 return;
460 }
b75a7d8f
A
461
462 engine->getGlyphs(fStyleRunInfo[run].glyphs, layoutStatus);
729e4ab9
A
463 if (LE_FAILURE(layoutStatus)) {
464 status = layoutStatus;
465 return;
466 }
467
b75a7d8f 468 engine->getGlyphPositions(fStyleRunInfo[run].positions, layoutStatus);
729e4ab9
A
469 if (LE_FAILURE(layoutStatus)) {
470 status = layoutStatus;
471 return;
472 }
473
b75a7d8f 474 engine->getCharIndices(&fGlyphToCharMap[glyphBase], runStart, layoutStatus);
729e4ab9
A
475 if (LE_FAILURE(layoutStatus)) {
476 status = layoutStatus;
477 return;
478 }
b75a7d8f
A
479
480 for (glyph = 0; glyph < glyphCount; glyph += 1) {
481 fGlyphWidths[glyphBase + glyph] = fStyleRunInfo[run].positions[glyph * 2 + 2] - fStyleRunInfo[run].positions[glyph * 2];
b75a7d8f
A
482 }
483
484 if ((fStyleRunInfo[run].level & 1) != 0) {
485 LXUtilities::reverse(&fGlyphWidths[glyphBase], glyphCount);
486 LXUtilities::reverse(&fGlyphToCharMap[glyphBase], glyphCount);
b75a7d8f
A
487 }
488
489 runStart = fStyleRunLimits[run];
490
491 delete engine;
492 fStyleRunInfo[run].engine = NULL;
493 }
494
b75a7d8f 495 fGlyphToCharMap[fGlyphCount] = fCharCount;
374ca955 496
729e4ab9
A
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
374ca955
A
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;
729e4ab9
A
520
521 // Now fill in the missing values in the char-to-glyph maps.
522 fillMissingCharToGlyphMapValues(fCharToMinGlyphMap, fCharCount);
523 fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap, fCharCount);
b75a7d8f
A
524}
525
526ParagraphLayout::~ParagraphLayout()
527{
528 delete (FontRuns *) fFontRuns;
529
530 if (! fClientLevels) {
531 delete (ValueRuns *) fLevelRuns;
532 fLevelRuns = NULL;
533
374ca955 534 fClientLevels = TRUE;
b75a7d8f
A
535 }
536
537 if (! fClientScripts) {
538 delete (ValueRuns *) fScriptRuns;
539 fScriptRuns = NULL;
540
374ca955 541 fClientScripts = TRUE;
b75a7d8f
A
542 }
543
544 if (! fClientLocales) {
545 delete (LocaleRuns *) fLocaleRuns;
546 fLocaleRuns = NULL;
547
374ca955 548 fClientLocales = TRUE;
b75a7d8f
A
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
374ca955
A
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;
b75a7d8f
A
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
57a6839d 614
b75a7d8f
A
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);
73c04bcf 620 le_bool result = FALSE;
b75a7d8f
A
621
622 while (uscript_nextRun(sr, NULL, NULL, &scriptCode)) {
623 if (isComplex(scriptCode)) {
73c04bcf
A
624 result = TRUE;
625 break;
b75a7d8f
A
626 }
627 }
628
73c04bcf
A
629 uscript_closeRun(sr);
630 return result;
b75a7d8f
A
631}
632
633le_int32 ParagraphLayout::getAscent() const
634{
374ca955 635 if (fAscent <= 0 && fCharCount > 0) {
b75a7d8f
A
636 ((ParagraphLayout *) this)->computeMetrics();
637 }
638
639 return fAscent;
640}
641
642le_int32 ParagraphLayout::getDescent() const
643{
374ca955 644 if (fAscent <= 0 && fCharCount > 0) {
b75a7d8f
A
645 ((ParagraphLayout *) this)->computeMetrics();
646 }
647
648 return fDescent;
649}
650
651le_int32 ParagraphLayout::getLeading() const
652{
374ca955 653 if (fAscent <= 0 && fCharCount > 0) {
b75a7d8f
A
654 ((ParagraphLayout *) this)->computeMetrics();
655 }
656
657 return fLeading;
658}
659
729e4ab9
A
660le_bool ParagraphLayout::isDone() const
661{
662 return fLineEnd >= fCharCount;
663}
664
b75a7d8f
A
665ParagraphLayout::Line *ParagraphLayout::nextLine(float width)
666{
729e4ab9 667 if (isDone()) {
b75a7d8f
A
668 return NULL;
669 }
670
671 fLineStart = fLineEnd;
672
673 if (width > 0) {
374ca955 674 le_int32 glyph = fCharToMinGlyphMap[fLineStart];
b75a7d8f
A
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
374ca955
A
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++];
b75a7d8f
A
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;
374ca955 746 fClientLevels = FALSE;
b75a7d8f
A
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;
374ca955 765 fClientScripts = FALSE;
b75a7d8f
A
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;
374ca955 776 fClientLocales = FALSE;
b75a7d8f
A
777}
778
374ca955 779void ParagraphLayout::computeSubFonts(const FontRuns *fontRuns, LEErrorCode &status)
b75a7d8f 780{
374ca955
A
781 if (LE_FAILURE(status)) {
782 return;
783 }
784
b75a7d8f
A
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]);
b75a7d8f
A
802
803 while (offset < styleRunLimits[run]) {
374ca955
A
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 }
b75a7d8f
A
810
811 subFontRuns->add(subFont, offset);
812 }
813
814 si += styleCount;
815 }
816
817 fFontRuns = subFontRuns;
818
374ca955 819cleanUp:
b75a7d8f
A
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{
46f4442e 865 {"afr", afkLanguageCode}, // Afrikaans
b75a7d8f
A
866 {"ara", araLanguageCode}, // Arabic
867 {"asm", asmLanguageCode}, // Assamese
46f4442e 868 {"bel", belLanguageCode}, // Belarussian
b75a7d8f 869 {"ben", benLanguageCode}, // Bengali
46f4442e
A
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
b75a7d8f 884 {"fas", farLanguageCode}, // Farsi
46f4442e
A
885 {"fin", finLanguageCode}, // Finnish
886 {"fra", fraLanguageCode}, // French
887 {"gle", gaeLanguageCode}, // Irish Gaelic
b75a7d8f 888 {"guj", gujLanguageCode}, // Gujarati
46f4442e 889 {"hau", hauLanguageCode}, // Hausa
b75a7d8f
A
890 {"heb", iwrLanguageCode}, // Hebrew
891 {"hin", hinLanguageCode}, // Hindi
46f4442e
A
892 {"hrv", hrvLanguageCode}, // Croatian
893 {"hun", hunLanguageCode}, // Hungarian
894 {"hye", hyeLanguageCode}, // Armenian
895 {"ind", indLanguageCode}, // Indonesian
896 {"ita", itaLanguageCode}, // Italian
b75a7d8f
A
897 {"jpn", janLanguageCode}, // Japanese
898 {"kan", kanLanguageCode}, // Kannada
899 {"kas", kshLanguageCode}, // Kashmiri
46f4442e 900 {"khm", khmLanguageCode}, // Khmer
b75a7d8f
A
901 {"kok", kokLanguageCode}, // Konkani
902 {"kor", korLanguageCode}, // Korean
903// {"mal_XXX", malLanguageCode}, // Malayalam - Traditional
904 {"mal", mlrLanguageCode}, // Malayalam - Reformed
905 {"mar", marLanguageCode}, // Marathi
46f4442e 906 {"mlt", mtsLanguageCode}, // Maltese
b75a7d8f 907 {"mni", mniLanguageCode}, // Manipuri
46f4442e
A
908 {"mon", mngLanguageCode}, // Mongolian
909 {"nep", nepLanguageCode}, // Nepali
b75a7d8f 910 {"ori", oriLanguageCode}, // Oriya
46f4442e
A
911 {"pol", plkLanguageCode}, // Polish
912 {"por", ptgLanguageCode}, // Portuguese
913 {"pus", pasLanguageCode}, // Pashto
914 {"ron", romLanguageCode}, // Romanian
915 {"rus", rusLanguageCode}, // Russian
b75a7d8f 916 {"san", sanLanguageCode}, // Sanskrit
b75a7d8f 917 {"sin", snhLanguageCode}, // Sinhalese
46f4442e
A
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
b75a7d8f
A
925 {"syr", syrLanguageCode}, // Syriac
926 {"tam", tamLanguageCode}, // Tamil
927 {"tel", telLanguageCode}, // Telugu
928 {"tha", thaLanguageCode}, // Thai
46f4442e 929 {"tur", trkLanguageCode}, // Turkish
b75a7d8f
A
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}
729e4ab9 964#else
b75a7d8f
A
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{
46f4442e 975 if (script < 0 || script >= (UScriptCode) scriptCodeCount) {
374ca955 976 return FALSE;
b75a7d8f
A
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) {
374ca955
A
1063 le_int32 minGlyph = fCharToMinGlyphMap[ch];
1064 le_int32 maxGlyph = fCharToMaxGlyphMap[ch];
b75a7d8f 1065
374ca955
A
1066 if (minGlyph < leftGlyph) {
1067 leftGlyph = minGlyph;
b75a7d8f
A
1068 }
1069
374ca955
A
1070 if (maxGlyph > rightGlyph) {
1071 rightGlyph = maxGlyph;
b75a7d8f
A
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
374ca955
A
1092 // run.
1093 fVisualRunLastX -= fStyleRunInfo[run].positions[leftGlyph * 2];
57a6839d 1094
b75a7d8f
A
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;
729e4ab9 1109 positions[outGlyph + 1] = fStyleRunInfo[run].positions[inGlyph + 1] + fVisualRunLastY;
b75a7d8f
A
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];
729e4ab9 1115 fVisualRunLastY = positions[outGlyph - 1];
b75a7d8f
A
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 {
46f4442e
A
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];
b75a7d8f
A
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
374ca955
A
1198le_int32 ParagraphLayout::Line::getWidth() const
1199{
1200 const VisualRun *lastRun = getVisualRun(fRunCount - 1);
46f4442e
A
1201
1202 if (lastRun == NULL) {
1203 return 0;
1204 }
1205
374ca955
A
1206 le_int32 glyphCount = lastRun->getGlyphCount();
1207 const float *positions = lastRun->getPositions();
57a6839d 1208
374ca955
A
1209 return (le_int32) positions[glyphCount * 2];
1210}
1211
b75a7d8f
A
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
374ca955
A
1269ParagraphLayout::VisualRun::~VisualRun()
1270{
1271 LE_DELETE_ARRAY(fGlyphToCharMap);
1272 LE_DELETE_ARRAY(fPositions);
1273 LE_DELETE_ARRAY(fGlyphs);
1274}
1275
b75a7d8f
A
1276U_NAMESPACE_END
1277
1278#endif
1279