2 **********************************************************************
3 * Copyright (C) 2002-2008, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
9 * paragraphLayout doesn't make much sense without
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"
18 #include "unicode/ubidi.h"
19 #include "unicode/uchriter.h"
20 #include "unicode/brkiter.h"
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! */
27 #include "layout/ParagraphLayout.h"
31 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
33 /* Leave this copyright notice here! It needs to go somewhere in this library. */
34 static const char copyright
[] = U_COPYRIGHT_STRING
;
39 StyleRuns(const RunArray
*styleRunArrays
[], le_int32 styleCount
);
43 le_int32
getRuns(le_int32 runLimits
[], le_int32 styleIndices
[]);
50 le_int32
*fStyleIndices
;
53 StyleRuns::StyleRuns(const RunArray
*styleRunArrays
[], le_int32 styleCount
)
54 : fStyleCount(styleCount
), fRunCount(0), fRunLimits(NULL
), fStyleIndices(NULL
)
56 le_int32 maxRunCount
= 0;
57 le_int32 style
, run
, runStyle
;
58 le_int32
*currentRun
= LE_NEW_ARRAY(le_int32
, styleCount
);
60 for (int i
= 0; i
< styleCount
; i
+= 1) {
61 maxRunCount
+= styleRunArrays
[i
]->getCount();
64 maxRunCount
-= styleCount
- 1;
66 fRunLimits
= LE_NEW_ARRAY(le_int32
, maxRunCount
);
67 fStyleIndices
= LE_NEW_ARRAY(le_int32
, maxRunCount
* styleCount
);
69 for (style
= 0; style
< styleCount
; style
+= 1) {
70 currentRun
[style
] = 0;
77 * Since the last run limit for each style run must be
78 * the same, all the styles will hit the last limit at
79 * the same time, so we know when we're done when the first
80 * style hits the last limit.
82 while (currentRun
[0] < styleRunArrays
[0]->getCount()) {
83 fRunLimits
[run
] = 0x7FFFFFFF;
85 // find the minimum run limit for all the styles
86 for (style
= 0; style
< styleCount
; style
+= 1) {
87 if (styleRunArrays
[style
]->getLimit(currentRun
[style
]) < fRunLimits
[run
]) {
88 fRunLimits
[run
] = styleRunArrays
[style
]->getLimit(currentRun
[style
]);
92 // advance all styles whose current run is at this limit to the next run
93 for (style
= 0; style
< styleCount
; style
+= 1) {
94 fStyleIndices
[runStyle
++] = currentRun
[style
];
96 if (styleRunArrays
[style
]->getLimit(currentRun
[style
]) == fRunLimits
[run
]) {
97 currentRun
[style
] += 1;
105 LE_DELETE_ARRAY(currentRun
);
108 StyleRuns::~StyleRuns()
112 LE_DELETE_ARRAY(fStyleIndices
);
113 fStyleIndices
= NULL
;
115 LE_DELETE_ARRAY(fRunLimits
);
119 le_int32
StyleRuns::getRuns(le_int32 runLimits
[], le_int32 styleIndices
[])
121 if (runLimits
!= NULL
) {
122 LE_ARRAY_COPY(runLimits
, fRunLimits
, fRunCount
);
125 if (styleIndices
!= NULL
) {
126 LE_ARRAY_COPY(styleIndices
, fStyleIndices
, fRunCount
* fStyleCount
);
133 * NOTE: This table only has "TRUE" values for
134 * those scripts which the LayoutEngine can currently
135 * process, rather for all scripts which require
136 * complex processing for correct rendering.
138 static const le_bool complexTable
[scriptCodeCount
] = {
146 FALSE
, /* Copt=Qaac */
272 const char ParagraphLayout::fgClassID
= 0;
275 * How to deal with composite fonts:
277 * Don't store the client's FontRuns; we'll need to compute sub-font FontRuns using Doug's
278 * LEFontInstance method. Do that by intersecting the client's FontRuns with fScriptRuns. Use
279 * that to compute fFontRuns, and then intersect fFontRuns, fScriptRuns and fLevelRuns. Doing
280 * it in this order means we do a two-way intersection and a three-way intersection.
282 * An optimization would be to only do this if there's at least one composite font...
286 * * Return the sub-fonts as the run fonts... could keep the mapping back to the client's FontRuns
287 * but that probably makes it more complicated of everyone...
289 * * Take the LineInfo and LineRun types from Paragraph and use them here, incorporate them into the API.
291 * * Might want to change the name of the StyleRun type, and make a new one that holds fonts, scripts and levels?
294 ParagraphLayout::ParagraphLayout(const LEUnicode chars
[], le_int32 count
,
295 const FontRuns
*fontRuns
,
296 const ValueRuns
*levelRuns
,
297 const ValueRuns
*scriptRuns
,
298 const LocaleRuns
*localeRuns
,
299 UBiDiLevel paragraphLevel
, le_bool vertical
,
301 : fChars(chars
), fCharCount(count
),
302 fFontRuns(NULL
), fLevelRuns(levelRuns
), fScriptRuns(scriptRuns
), fLocaleRuns(localeRuns
),
303 fVertical(vertical
), fClientLevels(TRUE
), fClientScripts(TRUE
), fClientLocales(TRUE
), fEmbeddingLevels(NULL
),
304 fAscent(0), fDescent(0), fLeading(0),
305 fGlyphToCharMap(NULL
), fCharToMinGlyphMap(NULL
), fCharToMaxGlyphMap(NULL
), fGlyphWidths(NULL
), fGlyphCount(0),
306 fParaBidi(NULL
), fLineBidi(NULL
),
307 fStyleRunLimits(NULL
), fStyleIndices(NULL
), fStyleRunCount(0),
308 fBreakIterator(NULL
), fLineStart(-1), fLineEnd(0),
309 /*fVisualRuns(NULL), fStyleRunInfo(NULL), fVisualRunCount(-1),
310 fFirstVisualRun(-1), fLastVisualRun(-1),*/ fVisualRunLastX(0), fVisualRunLastY(0)
313 if (LE_FAILURE(status
)) {
318 // FIXME: should check the limit arrays for consistency...
320 computeLevels(paragraphLevel
);
322 if (scriptRuns
== NULL
) {
326 if (localeRuns
== NULL
) {
330 computeSubFonts(fontRuns
, status
);
332 if (LE_FAILURE(status
)) {
338 // now intersect the font, direction and script runs...
339 const RunArray
*styleRunArrays
[] = {fFontRuns
, fLevelRuns
, fScriptRuns
, fLocaleRuns
};
340 le_int32 styleCount
= sizeof styleRunArrays
/ sizeof styleRunArrays
[0];
341 StyleRuns
styleRuns(styleRunArrays
, styleCount
);
342 LEErrorCode layoutStatus
= LE_NO_ERROR
;
344 fStyleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
346 fStyleRunLimits
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
);
347 fStyleIndices
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
* styleCount
);
349 styleRuns
.getRuns(fStyleRunLimits
, fStyleIndices
);
351 // now build a LayoutEngine for each style run...
352 le_int32
*styleIndices
= fStyleIndices
;
353 le_int32 run
, runStart
;
355 fStyleRunInfo
= LE_NEW_ARRAY(StyleRunInfo
, fStyleRunCount
);
358 for (runStart
= 0, run
= 0; run
< fStyleRunCount
; run
+= 1) {
359 fStyleRunInfo
[run
].font
= fFontRuns
->getFont(styleIndices
[0]);
360 fStyleRunInfo
[run
].runBase
= runStart
;
361 fStyleRunInfo
[run
].runLimit
= fStyleRunLimits
[run
];
362 fStyleRunInfo
[run
].script
= (UScriptCode
) fScriptRuns
->getValue(styleIndices
[2]);
363 fStyleRunInfo
[run
].locale
= fLocaleRuns
->getLocale(styleIndices
[3]);
364 fStyleRunInfo
[run
].level
= (UBiDiLevel
) fLevelRuns
->getValue(styleIndices
[1]);
365 fStyleRunInfo
[run
].glyphBase
= fGlyphCount
;
367 fStyleRunInfo
[run
].engine
= LayoutEngine::layoutEngineFactory(fStyleRunInfo
[run
].font
,
368 fStyleRunInfo
[run
].script
, getLanguageCode(fStyleRunInfo
[run
].locale
), layoutStatus
);
370 fStyleRunInfo
[run
].glyphCount
= fStyleRunInfo
[run
].engine
->layoutChars(fChars
, runStart
, fStyleRunLimits
[run
] - runStart
, fCharCount
,
371 fStyleRunInfo
[run
].level
& 1, 0, 0, layoutStatus
);
373 runStart
= fStyleRunLimits
[run
];
374 styleIndices
+= styleCount
;
375 fGlyphCount
+= fStyleRunInfo
[run
].glyphCount
;
378 // Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps,
379 // in logical order. (Both maps need an extra entry for the end of the text.)
381 // For each layout get the positions and convert them into glyph widths, in
382 // logical order. Get the glyph-to-char mapping, offset by starting index in the
383 // character array. Swap the glyph width and glyph-to-char arrays into logical order.
384 // Finally, fill in the char-to-glyph mappings.
385 fGlyphWidths
= LE_NEW_ARRAY(float, fGlyphCount
);
386 fGlyphToCharMap
= LE_NEW_ARRAY(le_int32
, fGlyphCount
+ 1);
387 fCharToMinGlyphMap
= LE_NEW_ARRAY(le_int32
, fCharCount
+ 1);
388 fCharToMaxGlyphMap
= LE_NEW_ARRAY(le_int32
, fCharCount
+ 1);
392 for (runStart
= 0, run
= 0; run
< fStyleRunCount
; run
+= 1) {
393 LayoutEngine
*engine
= fStyleRunInfo
[run
].engine
;
394 le_int32 glyphCount
= fStyleRunInfo
[run
].glyphCount
;
395 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
397 fStyleRunInfo
[run
].glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
398 fStyleRunInfo
[run
].positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
400 engine
->getGlyphs(fStyleRunInfo
[run
].glyphs
, layoutStatus
);
401 engine
->getGlyphPositions(fStyleRunInfo
[run
].positions
, layoutStatus
);
402 engine
->getCharIndices(&fGlyphToCharMap
[glyphBase
], runStart
, layoutStatus
);
404 for (glyph
= 0; glyph
< glyphCount
; glyph
+= 1) {
405 fGlyphWidths
[glyphBase
+ glyph
] = fStyleRunInfo
[run
].positions
[glyph
* 2 + 2] - fStyleRunInfo
[run
].positions
[glyph
* 2];
408 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
409 LXUtilities::reverse(&fGlyphWidths
[glyphBase
], glyphCount
);
410 LXUtilities::reverse(&fGlyphToCharMap
[glyphBase
], glyphCount
);
413 runStart
= fStyleRunLimits
[run
];
416 fStyleRunInfo
[run
].engine
= NULL
;
419 fGlyphToCharMap
[fGlyphCount
] = fCharCount
;
421 for (glyph
= fGlyphCount
- 1; glyph
>= 0; glyph
-= 1) {
422 le_int32 ch
= fGlyphToCharMap
[glyph
];
424 fCharToMinGlyphMap
[ch
] = glyph
;
427 fCharToMinGlyphMap
[fCharCount
] = fGlyphCount
;
429 for (glyph
= 0; glyph
< fGlyphCount
; glyph
+= 1) {
430 le_int32 ch
= fGlyphToCharMap
[glyph
];
432 fCharToMaxGlyphMap
[ch
] = glyph
;
435 fCharToMaxGlyphMap
[fCharCount
] = fGlyphCount
;
438 ParagraphLayout::~ParagraphLayout()
440 delete (FontRuns
*) fFontRuns
;
442 if (! fClientLevels
) {
443 delete (ValueRuns
*) fLevelRuns
;
446 fClientLevels
= TRUE
;
449 if (! fClientScripts
) {
450 delete (ValueRuns
*) fScriptRuns
;
453 fClientScripts
= TRUE
;
456 if (! fClientLocales
) {
457 delete (LocaleRuns
*) fLocaleRuns
;
460 fClientLocales
= TRUE
;
463 if (fEmbeddingLevels
!= NULL
) {
464 LE_DELETE_ARRAY(fEmbeddingLevels
);
465 fEmbeddingLevels
= NULL
;
468 if (fGlyphToCharMap
!= NULL
) {
469 LE_DELETE_ARRAY(fGlyphToCharMap
);
470 fGlyphToCharMap
= NULL
;
473 if (fCharToMinGlyphMap
!= NULL
) {
474 LE_DELETE_ARRAY(fCharToMinGlyphMap
);
475 fCharToMinGlyphMap
= NULL
;
478 if (fCharToMaxGlyphMap
!= NULL
) {
479 LE_DELETE_ARRAY(fCharToMaxGlyphMap
);
480 fCharToMaxGlyphMap
= NULL
;
483 if (fGlyphWidths
!= NULL
) {
484 LE_DELETE_ARRAY(fGlyphWidths
);
488 if (fParaBidi
!= NULL
) {
489 ubidi_close(fParaBidi
);
493 if (fLineBidi
!= NULL
) {
494 ubidi_close(fLineBidi
);
498 if (fStyleRunCount
> 0) {
501 LE_DELETE_ARRAY(fStyleRunLimits
);
502 LE_DELETE_ARRAY(fStyleIndices
);
504 for (run
= 0; run
< fStyleRunCount
; run
+= 1) {
505 LE_DELETE_ARRAY(fStyleRunInfo
[run
].glyphs
);
506 LE_DELETE_ARRAY(fStyleRunInfo
[run
].positions
);
508 fStyleRunInfo
[run
].glyphs
= NULL
;
509 fStyleRunInfo
[run
].positions
= NULL
;
512 LE_DELETE_ARRAY(fStyleRunInfo
);
514 fStyleRunLimits
= NULL
;
515 fStyleIndices
= NULL
;
516 fStyleRunInfo
= NULL
;
520 if (fBreakIterator
!= NULL
) {
521 delete fBreakIterator
;
522 fBreakIterator
= NULL
;
527 le_bool
ParagraphLayout::isComplex(const LEUnicode chars
[], le_int32 count
)
529 UErrorCode scriptStatus
= U_ZERO_ERROR
;
530 UScriptCode scriptCode
= USCRIPT_INVALID_CODE
;
531 UScriptRun
*sr
= uscript_openRun(chars
, count
, &scriptStatus
);
532 le_bool result
= FALSE
;
534 while (uscript_nextRun(sr
, NULL
, NULL
, &scriptCode
)) {
535 if (isComplex(scriptCode
)) {
541 uscript_closeRun(sr
);
545 le_int32
ParagraphLayout::getAscent() const
547 if (fAscent
<= 0 && fCharCount
> 0) {
548 ((ParagraphLayout
*) this)->computeMetrics();
554 le_int32
ParagraphLayout::getDescent() const
556 if (fAscent
<= 0 && fCharCount
> 0) {
557 ((ParagraphLayout
*) this)->computeMetrics();
563 le_int32
ParagraphLayout::getLeading() const
565 if (fAscent
<= 0 && fCharCount
> 0) {
566 ((ParagraphLayout
*) this)->computeMetrics();
572 ParagraphLayout::Line
*ParagraphLayout::nextLine(float width
)
574 if (fLineEnd
>= fCharCount
) {
578 fLineStart
= fLineEnd
;
581 le_int32 glyph
= fCharToMinGlyphMap
[fLineStart
];
582 float widthSoFar
= 0;
584 while (glyph
< fGlyphCount
&& widthSoFar
+ fGlyphWidths
[glyph
] <= width
) {
585 widthSoFar
+= fGlyphWidths
[glyph
++];
588 // If no glyphs fit on the line, force one to fit.
590 // (There shouldn't be any zero width glyphs at the
591 // start of a line unless the paragraph consists of
592 // only zero width glyphs, because otherwise the zero
593 // width glyphs will have been included on the end of
594 // the previous line...)
595 if (widthSoFar
== 0 && glyph
< fGlyphCount
) {
599 fLineEnd
= previousBreak(fGlyphToCharMap
[glyph
]);
601 // If this break is at or before the last one,
602 // find a glyph, starting at the one which didn't
603 // fit, that produces a break after the last one.
604 while (fLineEnd
<= fLineStart
) {
605 fLineEnd
= fGlyphToCharMap
[glyph
++];
608 fLineEnd
= fCharCount
;
611 return computeVisualRuns();
614 void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel
)
616 UErrorCode bidiStatus
= U_ZERO_ERROR
;
618 if (fLevelRuns
!= NULL
) {
622 fEmbeddingLevels
= LE_NEW_ARRAY(UBiDiLevel
, fCharCount
);
624 for (ch
= 0, run
= 0; run
< fLevelRuns
->getCount(); run
+= 1) {
625 UBiDiLevel runLevel
= (UBiDiLevel
) fLevelRuns
->getValue(run
) | UBIDI_LEVEL_OVERRIDE
;
626 le_int32 runLimit
= fLevelRuns
->getLimit(run
);
628 while (ch
< runLimit
) {
629 fEmbeddingLevels
[ch
++] = runLevel
;
634 fParaBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
635 ubidi_setPara(fParaBidi
, fChars
, fCharCount
, paragraphLevel
, fEmbeddingLevels
, &bidiStatus
);
637 if (fLevelRuns
== NULL
) {
638 le_int32 levelRunCount
= ubidi_countRuns(fParaBidi
, &bidiStatus
);
639 ValueRuns
*levelRuns
= new ValueRuns(levelRunCount
);
641 le_int32 logicalStart
= 0;
646 for (run
= 0; run
< levelRunCount
; run
+= 1) {
647 ubidi_getLogicalRun(fParaBidi
, logicalStart
, &limit
, &level
);
648 levelRuns
->add(level
, limit
);
649 logicalStart
= limit
;
652 fLevelRuns
= levelRuns
;
653 fClientLevels
= FALSE
;
657 void ParagraphLayout::computeScripts()
659 UErrorCode scriptStatus
= U_ZERO_ERROR
;
660 UScriptRun
*sr
= uscript_openRun(fChars
, fCharCount
, &scriptStatus
);
661 ValueRuns
*scriptRuns
= new ValueRuns(0);
665 while (uscript_nextRun(sr
, NULL
, &limit
, &script
)) {
666 scriptRuns
->add(script
, limit
);
669 uscript_closeRun(sr
);
671 fScriptRuns
= scriptRuns
;
672 fClientScripts
= FALSE
;
675 void ParagraphLayout::computeLocales()
677 LocaleRuns
*localeRuns
= new LocaleRuns(0);
678 const Locale
*defaultLocale
= &Locale::getDefault();
680 localeRuns
->add(defaultLocale
, fCharCount
);
682 fLocaleRuns
= localeRuns
;
683 fClientLocales
= FALSE
;
686 void ParagraphLayout::computeSubFonts(const FontRuns
*fontRuns
, LEErrorCode
&status
)
688 if (LE_FAILURE(status
)) {
692 const RunArray
*styleRunArrays
[] = {fontRuns
, fScriptRuns
};
693 le_int32 styleCount
= sizeof styleRunArrays
/ sizeof styleRunArrays
[0];
694 StyleRuns
styleRuns(styleRunArrays
, styleCount
);
695 le_int32 styleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
696 le_int32
*styleRunLimits
= LE_NEW_ARRAY(le_int32
, styleRunCount
);
697 le_int32
*styleIndices
= LE_NEW_ARRAY(le_int32
, styleRunCount
* styleCount
);
698 FontRuns
*subFontRuns
= new FontRuns(0);
699 le_int32 run
, offset
, *si
;
701 styleRuns
.getRuns(styleRunLimits
, styleIndices
);
706 for (run
= 0; run
< styleRunCount
; run
+= 1) {
707 const LEFontInstance
*runFont
= fontRuns
->getFont(si
[0]);
708 le_int32 script
= fScriptRuns
->getValue(si
[1]);
710 while (offset
< styleRunLimits
[run
]) {
711 const LEFontInstance
*subFont
= runFont
->getSubFont(fChars
, &offset
, styleRunLimits
[run
], script
, status
);
713 if (LE_FAILURE(status
)) {
718 subFontRuns
->add(subFont
, offset
);
724 fFontRuns
= subFontRuns
;
727 LE_DELETE_ARRAY(styleIndices
);
728 LE_DELETE_ARRAY(styleRunLimits
);
731 void ParagraphLayout::computeMetrics()
733 le_int32 i
, count
= fFontRuns
->getCount();
736 for (i
= 0; i
< count
; i
+= 1) {
737 const LEFontInstance
*font
= fFontRuns
->getFont(i
);
738 le_int32 ascent
= font
->getAscent();
739 le_int32 descent
= font
->getDescent();
740 le_int32 leading
= font
->getLeading();
741 le_int32 dl
= descent
+ leading
;
743 if (ascent
> fAscent
) {
747 if (descent
> fDescent
) {
751 if (leading
> fLeading
) {
760 fLeading
= maxDL
- fDescent
;
766 const char *localeCode
;
767 le_int32 languageCode
;
770 static const LanguageMap languageMap
[] =
772 {"afr", afkLanguageCode
}, // Afrikaans
773 {"ara", araLanguageCode
}, // Arabic
774 {"asm", asmLanguageCode
}, // Assamese
775 {"bel", belLanguageCode
}, // Belarussian
776 {"ben", benLanguageCode
}, // Bengali
777 {"bod", tibLanguageCode
}, // Tibetan
778 {"bul", bgrLanguageCode
}, // Bulgarian
779 {"cat", catLanguageCode
}, // Catalan
780 {"ces", csyLanguageCode
}, // Czech
781 {"che", cheLanguageCode
}, // Chechen
782 {"cop", copLanguageCode
}, // Coptic
783 {"cym", welLanguageCode
}, // Welsh
784 {"dan", danLanguageCode
}, // Danish
785 {"deu", deuLanguageCode
}, // German
786 {"dzo", dznLanguageCode
}, // Dzongkha
787 {"ell", ellLanguageCode
}, // Greek
788 {"eng", engLanguageCode
}, // English
789 {"est", etiLanguageCode
}, // Estonian
790 {"eus", euqLanguageCode
}, // Basque
791 {"fas", farLanguageCode
}, // Farsi
792 {"fin", finLanguageCode
}, // Finnish
793 {"fra", fraLanguageCode
}, // French
794 {"gle", gaeLanguageCode
}, // Irish Gaelic
795 {"guj", gujLanguageCode
}, // Gujarati
796 {"hau", hauLanguageCode
}, // Hausa
797 {"heb", iwrLanguageCode
}, // Hebrew
798 {"hin", hinLanguageCode
}, // Hindi
799 {"hrv", hrvLanguageCode
}, // Croatian
800 {"hun", hunLanguageCode
}, // Hungarian
801 {"hye", hyeLanguageCode
}, // Armenian
802 {"ind", indLanguageCode
}, // Indonesian
803 {"ita", itaLanguageCode
}, // Italian
804 {"jpn", janLanguageCode
}, // Japanese
805 {"kan", kanLanguageCode
}, // Kannada
806 {"kas", kshLanguageCode
}, // Kashmiri
807 {"khm", khmLanguageCode
}, // Khmer
808 {"kok", kokLanguageCode
}, // Konkani
809 {"kor", korLanguageCode
}, // Korean
810 // {"mal_XXX", malLanguageCode}, // Malayalam - Traditional
811 {"mal", mlrLanguageCode
}, // Malayalam - Reformed
812 {"mar", marLanguageCode
}, // Marathi
813 {"mlt", mtsLanguageCode
}, // Maltese
814 {"mni", mniLanguageCode
}, // Manipuri
815 {"mon", mngLanguageCode
}, // Mongolian
816 {"nep", nepLanguageCode
}, // Nepali
817 {"ori", oriLanguageCode
}, // Oriya
818 {"pol", plkLanguageCode
}, // Polish
819 {"por", ptgLanguageCode
}, // Portuguese
820 {"pus", pasLanguageCode
}, // Pashto
821 {"ron", romLanguageCode
}, // Romanian
822 {"rus", rusLanguageCode
}, // Russian
823 {"san", sanLanguageCode
}, // Sanskrit
824 {"sin", snhLanguageCode
}, // Sinhalese
825 {"slk", skyLanguageCode
}, // Slovak
826 {"snd", sndLanguageCode
}, // Sindhi
827 {"slv", slvLanguageCode
}, // Slovenian
828 {"spa", espLanguageCode
}, // Spanish
829 {"sqi", sqiLanguageCode
}, // Albanian
830 {"srp", srbLanguageCode
}, // Serbian
831 {"swe", sveLanguageCode
}, // Swedish
832 {"syr", syrLanguageCode
}, // Syriac
833 {"tam", tamLanguageCode
}, // Tamil
834 {"tel", telLanguageCode
}, // Telugu
835 {"tha", thaLanguageCode
}, // Thai
836 {"tur", trkLanguageCode
}, // Turkish
837 {"urd", urdLanguageCode
}, // Urdu
838 {"yid", jiiLanguageCode
}, // Yiddish
839 // {"zhp", zhpLanguageCode}, // Chinese - Phonetic
840 {"zho", zhsLanguageCode
}, // Chinese
841 {"zho_CHN", zhsLanguageCode
}, // Chinese - China
842 {"zho_HKG", zhsLanguageCode
}, // Chinese - Hong Kong
843 {"zho_MAC", zhtLanguageCode
}, // Chinese - Macao
844 {"zho_SGP", zhsLanguageCode
}, // Chinese - Singapore
845 {"zho_TWN", zhtLanguageCode
} // Chinese - Taiwan
848 static const le_int32 languageMapCount
= ARRAY_SIZE(languageMap
);
850 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
852 char code
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
853 const char *language
= locale
->getISO3Language();
854 const char *country
= locale
->getISO3Country();
856 uprv_strcat(code
, language
);
858 if ((uprv_strcmp(language
, "zho") == 0) && country
!= NULL
) {
859 uprv_strcat(code
, "_");
860 uprv_strcat(code
, country
);
863 for (le_int32 i
= 0; i
< languageMapCount
; i
+= 1) {
864 if (uprv_strcmp(code
, languageMap
[i
].localeCode
) == 0) {
865 return languageMap
[i
].languageCode
;
869 return nullLanguageCode
;
873 // TODO - dummy implementation for right now...
874 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
876 return nullLanguageCode
;
880 le_bool
ParagraphLayout::isComplex(UScriptCode script
)
882 if (script
< 0 || script
>= (UScriptCode
) scriptCodeCount
) {
886 return complexTable
[script
];
889 le_int32
ParagraphLayout::previousBreak(le_int32 charIndex
)
891 // skip over any whitespace or control characters,
892 // because they can hang in the margin.
893 while (charIndex
< fCharCount
&&
894 (u_isWhitespace(fChars
[charIndex
]) ||
895 u_iscntrl(fChars
[charIndex
]))) {
899 // Create the BreakIterator if we don't already have one
900 if (fBreakIterator
== NULL
) {
902 UCharCharacterIterator
*iter
= new UCharCharacterIterator(fChars
, fCharCount
);
903 UErrorCode status
= U_ZERO_ERROR
;
905 fBreakIterator
= BreakIterator::createLineInstance(thai
, status
);
906 fBreakIterator
->adoptText(iter
);
909 // return the break location that's at or before
910 // the character we stopped on. Note: if we're
911 // on a break, the "+ 1" will cause preceding to
913 return fBreakIterator
->preceding(charIndex
+ 1);
916 ParagraphLayout::Line
*ParagraphLayout::computeVisualRuns()
918 UErrorCode bidiStatus
= U_ZERO_ERROR
;
919 le_int32 dirRunCount
, visualRun
;
923 fFirstVisualRun
= getCharRun(fLineStart
);
924 fLastVisualRun
= getCharRun(fLineEnd
- 1);
926 if (fLineBidi
== NULL
) {
927 fLineBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
930 ubidi_setLine(fParaBidi
, fLineStart
, fLineEnd
, fLineBidi
, &bidiStatus
);
931 dirRunCount
= ubidi_countRuns(fLineBidi
, &bidiStatus
);
933 Line
*line
= new Line();
935 for (visualRun
= 0; visualRun
< dirRunCount
; visualRun
+= 1) {
936 le_int32 relStart
, run
, runLength
;
937 UBiDiDirection runDirection
= ubidi_getVisualRun(fLineBidi
, visualRun
, &relStart
, &runLength
);
938 le_int32 runStart
= fLineStart
+ relStart
;
939 le_int32 runEnd
= runStart
+ runLength
- 1;
940 le_int32 firstRun
= getCharRun(runStart
);
941 le_int32 lastRun
= getCharRun(runEnd
);
942 le_int32 startRun
= (runDirection
== UBIDI_LTR
)? firstRun
: lastRun
;
943 le_int32 stopRun
= (runDirection
== UBIDI_LTR
)? lastRun
+ 1 : firstRun
- 1;
944 le_int32 dir
= (runDirection
== UBIDI_LTR
)? 1 : -1;
946 for (run
= startRun
; run
!= stopRun
; run
+= dir
) {
947 le_int32 firstChar
= (run
== firstRun
)? runStart
: fStyleRunInfo
[run
].runBase
;
948 le_int32 lastChar
= (run
== lastRun
)? runEnd
: fStyleRunInfo
[run
].runLimit
- 1;
950 appendRun(line
, run
, firstChar
, lastChar
);
957 void ParagraphLayout::appendRun(ParagraphLayout::Line
*line
, le_int32 run
, le_int32 firstChar
, le_int32 lastChar
)
959 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
960 le_int32 inGlyph
, outGlyph
;
962 // Get the glyph indices for all the characters between firstChar and lastChar,
963 // make the minimum one be leftGlyph and the maximum one be rightGlyph.
964 // (need to do this to handle local reorderings like Indic left matras)
965 le_int32 leftGlyph
= fGlyphCount
;
966 le_int32 rightGlyph
= -1;
969 for (ch
= firstChar
; ch
<= lastChar
; ch
+= 1) {
970 le_int32 minGlyph
= fCharToMinGlyphMap
[ch
];
971 le_int32 maxGlyph
= fCharToMaxGlyphMap
[ch
];
973 if (minGlyph
< leftGlyph
) {
974 leftGlyph
= minGlyph
;
977 if (maxGlyph
> rightGlyph
) {
978 rightGlyph
= maxGlyph
;
982 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
983 le_int32 swap
= rightGlyph
;
984 le_int32 last
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
986 // Here, we want to remove the glyphBase bias...
987 rightGlyph
= last
- leftGlyph
;
988 leftGlyph
= last
- swap
;
990 rightGlyph
-= glyphBase
;
991 leftGlyph
-= glyphBase
;
994 // Set the position bias for the glyphs. If we're at the start of
995 // a line, we want the first glyph to be at x = 0, even if it comes
996 // from the middle of a layout. If we've got a right-to-left run, we
997 // want the left-most glyph to start at the final x position of the
998 // previous run, even though this glyph may be in the middle of the
1000 fVisualRunLastX
-= fStyleRunInfo
[run
].positions
[leftGlyph
* 2];
1002 // Make rightGlyph be the glyph just to the right of
1006 UBiDiDirection direction
= ((fStyleRunInfo
[run
].level
& 1) == 0)? UBIDI_LTR
: UBIDI_RTL
;
1007 le_int32 glyphCount
= rightGlyph
- leftGlyph
;
1008 LEGlyphID
*glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
1009 float *positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
1010 le_int32
*glyphToCharMap
= LE_NEW_ARRAY(le_int32
, glyphCount
);
1012 LE_ARRAY_COPY(glyphs
, &fStyleRunInfo
[run
].glyphs
[leftGlyph
], glyphCount
);
1014 for (outGlyph
= 0, inGlyph
= leftGlyph
* 2; inGlyph
<= rightGlyph
* 2; inGlyph
+= 2, outGlyph
+= 2) {
1015 positions
[outGlyph
] = fStyleRunInfo
[run
].positions
[inGlyph
] + fVisualRunLastX
;
1016 positions
[outGlyph
+ 1] = fStyleRunInfo
[run
].positions
[inGlyph
+ 1] /* + fVisualRunLastY */;
1019 // Save the ending position of this run
1020 // to use for the start of the next run
1021 fVisualRunLastX
= positions
[outGlyph
- 2];
1022 // fVisualRunLastY = positions[rightGlyph * 2 + 2];
1024 if ((fStyleRunInfo
[run
].level
& 1) == 0) {
1025 for (outGlyph
= 0, inGlyph
= leftGlyph
; inGlyph
< rightGlyph
; inGlyph
+= 1, outGlyph
+= 1) {
1026 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[glyphBase
+ inGlyph
];
1029 // Because fGlyphToCharMap is stored in logical order to facilitate line breaking,
1030 // we need to map the physical glyph indices to logical indices while we copy the
1031 // character indices.
1032 le_int32 base
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
1034 for (outGlyph
= 0, inGlyph
= leftGlyph
; inGlyph
< rightGlyph
; inGlyph
+= 1, outGlyph
+= 1) {
1035 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[base
- inGlyph
];
1039 line
->append(fStyleRunInfo
[run
].font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1042 le_int32
ParagraphLayout::getCharRun(le_int32 charIndex
)
1044 if (charIndex
< 0 || charIndex
> fCharCount
) {
1050 // NOTE: as long as fStyleRunLimits is well-formed
1051 // the above range check guarantees that we'll never
1052 // fall off the end of the array.
1054 while (charIndex
>= fStyleRunLimits
[run
]) {
1062 const char ParagraphLayout::Line::fgClassID
= 0;
1064 #define INITIAL_RUN_CAPACITY 4
1065 #define RUN_CAPACITY_GROW_LIMIT 16
1067 ParagraphLayout::Line::~Line()
1071 for (i
= 0; i
< fRunCount
; i
+= 1) {
1075 LE_DELETE_ARRAY(fRuns
);
1078 le_int32
ParagraphLayout::Line::getAscent() const
1081 ((ParagraphLayout::Line
*)this)->computeMetrics();
1087 le_int32
ParagraphLayout::Line::getDescent() const
1090 ((ParagraphLayout::Line
*)this)->computeMetrics();
1096 le_int32
ParagraphLayout::Line::getLeading() const
1099 ((ParagraphLayout::Line
*)this)->computeMetrics();
1105 le_int32
ParagraphLayout::Line::getWidth() const
1107 const VisualRun
*lastRun
= getVisualRun(fRunCount
- 1);
1109 if (lastRun
== NULL
) {
1113 le_int32 glyphCount
= lastRun
->getGlyphCount();
1114 const float *positions
= lastRun
->getPositions();
1116 return (le_int32
) positions
[glyphCount
* 2];
1119 const ParagraphLayout::VisualRun
*ParagraphLayout::Line::getVisualRun(le_int32 runIndex
) const
1121 if (runIndex
< 0 || runIndex
>= fRunCount
) {
1125 return fRuns
[runIndex
];
1128 void ParagraphLayout::Line::append(const LEFontInstance
*font
, UBiDiDirection direction
, le_int32 glyphCount
,
1129 const LEGlyphID glyphs
[], const float positions
[], const le_int32 glyphToCharMap
[])
1131 if (fRunCount
>= fRunCapacity
) {
1132 if (fRunCapacity
== 0) {
1133 fRunCapacity
= INITIAL_RUN_CAPACITY
;
1134 fRuns
= LE_NEW_ARRAY(ParagraphLayout::VisualRun
*, fRunCapacity
);
1136 fRunCapacity
+= (fRunCapacity
< RUN_CAPACITY_GROW_LIMIT
? fRunCapacity
: RUN_CAPACITY_GROW_LIMIT
);
1137 fRuns
= (ParagraphLayout::VisualRun
**) LE_GROW_ARRAY(fRuns
, fRunCapacity
);
1141 fRuns
[fRunCount
++] = new ParagraphLayout::VisualRun(font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1144 void ParagraphLayout::Line::computeMetrics()
1148 for (le_int32 i
= 0; i
< fRunCount
; i
+= 1) {
1149 le_int32 ascent
= fRuns
[i
]->getAscent();
1150 le_int32 descent
= fRuns
[i
]->getDescent();
1151 le_int32 leading
= fRuns
[i
]->getLeading();
1152 le_int32 dl
= descent
+ leading
;
1154 if (ascent
> fAscent
) {
1158 if (descent
> fDescent
) {
1162 if (leading
> fLeading
) {
1171 fLeading
= maxDL
- fDescent
;
1174 const char ParagraphLayout::VisualRun::fgClassID
= 0;
1176 ParagraphLayout::VisualRun::~VisualRun()
1178 LE_DELETE_ARRAY(fGlyphToCharMap
);
1179 LE_DELETE_ARRAY(fPositions
);
1180 LE_DELETE_ARRAY(fGlyphs
);