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