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