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