2 **********************************************************************
3 * Copyright (C) 2002-2004, 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/LELanguages.h"
14 #include "layout/LayoutEngine.h"
15 #include "layout/LEFontInstance.h"
17 #include "unicode/ubidi.h"
18 #include "unicode/uchriter.h"
19 #include "unicode/brkiter.h"
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! */
26 #include "layout/ParagraphLayout.h"
30 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
35 StyleRuns(const RunArray
*styleRunArrays
[], le_int32 styleCount
);
39 le_int32
getRuns(le_int32 runLimits
[], le_int32 styleIndices
[]);
46 le_int32
*fStyleIndices
;
49 StyleRuns::StyleRuns(const RunArray
*styleRunArrays
[], le_int32 styleCount
)
50 : fStyleCount(styleCount
), fRunCount(0), fRunLimits(NULL
), fStyleIndices(NULL
)
52 le_int32 maxRunCount
= 0;
53 le_int32 style
, run
, runStyle
;
54 le_int32
*currentRun
= LE_NEW_ARRAY(le_int32
, styleCount
);
56 for (int i
= 0; i
< styleCount
; i
+= 1) {
57 maxRunCount
+= styleRunArrays
[i
]->getCount();
60 maxRunCount
-= styleCount
- 1;
62 fRunLimits
= LE_NEW_ARRAY(le_int32
, maxRunCount
);
63 fStyleIndices
= LE_NEW_ARRAY(le_int32
, maxRunCount
* styleCount
);
65 for (style
= 0; style
< styleCount
; style
+= 1) {
66 currentRun
[style
] = 0;
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.
78 while (currentRun
[0] < styleRunArrays
[0]->getCount()) {
79 fRunLimits
[run
] = 0x7FFFFFFF;
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
]);
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
];
92 if (styleRunArrays
[style
]->getLimit(currentRun
[style
]) == fRunLimits
[run
]) {
93 currentRun
[style
] += 1;
101 LE_DELETE_ARRAY(currentRun
);
104 StyleRuns::~StyleRuns()
108 LE_DELETE_ARRAY(fStyleIndices
);
109 fStyleIndices
= NULL
;
111 LE_DELETE_ARRAY(fRunLimits
);
115 le_int32
StyleRuns::getRuns(le_int32 runLimits
[], le_int32 styleIndices
[])
117 if (runLimits
!= NULL
) {
118 LE_ARRAY_COPY(runLimits
, fRunLimits
, fRunCount
);
121 if (styleIndices
!= NULL
) {
122 LE_ARRAY_COPY(styleIndices
, fStyleIndices
, fRunCount
* fStyleCount
);
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.
134 static const le_bool complexTable
[] = {
193 const char ParagraphLayout::fgClassID
= 0;
196 * How to deal with composite fonts:
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.
203 * An optimization would be to only do this if there's at least one composite font...
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...
210 * * Take the LineInfo and LineRun types from Paragraph and use them here, incorporate them into the API.
212 * * Might want to change the name of the StyleRun type, and make a new one that holds fonts, scripts and levels?
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
,
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)
234 if (LE_FAILURE(status
)) {
239 // FIXME: should check the limit arrays for consistency...
241 computeLevels(paragraphLevel
);
243 if (scriptRuns
== NULL
) {
247 if (localeRuns
== NULL
) {
251 computeSubFonts(fontRuns
, status
);
253 if (LE_FAILURE(status
)) {
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
;
265 fStyleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
267 fStyleRunLimits
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
);
268 fStyleIndices
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
* styleCount
);
270 styleRuns
.getRuns(fStyleRunLimits
, fStyleIndices
);
272 // now build a LayoutEngine for each style run...
273 le_int32
*styleIndices
= fStyleIndices
;
274 le_int32 run
, runStart
;
276 fStyleRunInfo
= LE_NEW_ARRAY(StyleRunInfo
, fStyleRunCount
);
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
;
288 fStyleRunInfo
[run
].engine
= LayoutEngine::layoutEngineFactory(fStyleRunInfo
[run
].font
,
289 fStyleRunInfo
[run
].script
, getLanguageCode(fStyleRunInfo
[run
].locale
), layoutStatus
);
291 fStyleRunInfo
[run
].glyphCount
= fStyleRunInfo
[run
].engine
->layoutChars(fChars
, runStart
, fStyleRunLimits
[run
] - runStart
, fCharCount
,
292 fStyleRunInfo
[run
].level
& 1, 0, 0, layoutStatus
);
294 runStart
= fStyleRunLimits
[run
];
295 styleIndices
+= styleCount
;
296 fGlyphCount
+= fStyleRunInfo
[run
].glyphCount
;
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.)
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);
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
;
318 fStyleRunInfo
[run
].glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
319 fStyleRunInfo
[run
].positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
321 engine
->getGlyphs(fStyleRunInfo
[run
].glyphs
, layoutStatus
);
322 engine
->getGlyphPositions(fStyleRunInfo
[run
].positions
, layoutStatus
);
323 engine
->getCharIndices(&fGlyphToCharMap
[glyphBase
], runStart
, layoutStatus
);
325 for (glyph
= 0; glyph
< glyphCount
; glyph
+= 1) {
326 fGlyphWidths
[glyphBase
+ glyph
] = fStyleRunInfo
[run
].positions
[glyph
* 2 + 2] - fStyleRunInfo
[run
].positions
[glyph
* 2];
329 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
330 LXUtilities::reverse(&fGlyphWidths
[glyphBase
], glyphCount
);
331 LXUtilities::reverse(&fGlyphToCharMap
[glyphBase
], glyphCount
);
334 runStart
= fStyleRunLimits
[run
];
337 fStyleRunInfo
[run
].engine
= NULL
;
340 fGlyphToCharMap
[fGlyphCount
] = fCharCount
;
342 for (glyph
= fGlyphCount
- 1; glyph
>= 0; glyph
-= 1) {
343 le_int32 ch
= fGlyphToCharMap
[glyph
];
345 fCharToMinGlyphMap
[ch
] = glyph
;
348 fCharToMinGlyphMap
[fCharCount
] = fGlyphCount
;
350 for (glyph
= 0; glyph
< fGlyphCount
; glyph
+= 1) {
351 le_int32 ch
= fGlyphToCharMap
[glyph
];
353 fCharToMaxGlyphMap
[ch
] = glyph
;
356 fCharToMaxGlyphMap
[fCharCount
] = fGlyphCount
;
359 ParagraphLayout::~ParagraphLayout()
361 delete (FontRuns
*) fFontRuns
;
363 if (! fClientLevels
) {
364 delete (ValueRuns
*) fLevelRuns
;
367 fClientLevels
= TRUE
;
370 if (! fClientScripts
) {
371 delete (ValueRuns
*) fScriptRuns
;
374 fClientScripts
= TRUE
;
377 if (! fClientLocales
) {
378 delete (LocaleRuns
*) fLocaleRuns
;
381 fClientLocales
= TRUE
;
384 if (fEmbeddingLevels
!= NULL
) {
385 LE_DELETE_ARRAY(fEmbeddingLevels
);
386 fEmbeddingLevels
= NULL
;
389 if (fGlyphToCharMap
!= NULL
) {
390 LE_DELETE_ARRAY(fGlyphToCharMap
);
391 fGlyphToCharMap
= NULL
;
394 if (fCharToMinGlyphMap
!= NULL
) {
395 LE_DELETE_ARRAY(fCharToMinGlyphMap
);
396 fCharToMinGlyphMap
= NULL
;
399 if (fCharToMaxGlyphMap
!= NULL
) {
400 LE_DELETE_ARRAY(fCharToMaxGlyphMap
);
401 fCharToMaxGlyphMap
= NULL
;
404 if (fGlyphWidths
!= NULL
) {
405 LE_DELETE_ARRAY(fGlyphWidths
);
409 if (fParaBidi
!= NULL
) {
410 ubidi_close(fParaBidi
);
414 if (fLineBidi
!= NULL
) {
415 ubidi_close(fLineBidi
);
419 if (fStyleRunCount
> 0) {
422 LE_DELETE_ARRAY(fStyleRunLimits
);
423 LE_DELETE_ARRAY(fStyleIndices
);
425 for (run
= 0; run
< fStyleRunCount
; run
+= 1) {
426 LE_DELETE_ARRAY(fStyleRunInfo
[run
].glyphs
);
427 LE_DELETE_ARRAY(fStyleRunInfo
[run
].positions
);
429 fStyleRunInfo
[run
].glyphs
= NULL
;
430 fStyleRunInfo
[run
].positions
= NULL
;
433 LE_DELETE_ARRAY(fStyleRunInfo
);
435 fStyleRunLimits
= NULL
;
436 fStyleIndices
= NULL
;
437 fStyleRunInfo
= NULL
;
441 if (fBreakIterator
!= NULL
) {
442 delete fBreakIterator
;
443 fBreakIterator
= NULL
;
448 le_bool
ParagraphLayout::isComplex(const LEUnicode chars
[], le_int32 count
)
450 UErrorCode scriptStatus
= U_ZERO_ERROR
;
451 UScriptCode scriptCode
= USCRIPT_INVALID_CODE
;
452 UScriptRun
*sr
= uscript_openRun(chars
, count
, &scriptStatus
);
454 while (uscript_nextRun(sr
, NULL
, NULL
, &scriptCode
)) {
455 if (isComplex(scriptCode
)) {
463 le_int32
ParagraphLayout::getAscent() const
465 if (fAscent
<= 0 && fCharCount
> 0) {
466 ((ParagraphLayout
*) this)->computeMetrics();
472 le_int32
ParagraphLayout::getDescent() const
474 if (fAscent
<= 0 && fCharCount
> 0) {
475 ((ParagraphLayout
*) this)->computeMetrics();
481 le_int32
ParagraphLayout::getLeading() const
483 if (fAscent
<= 0 && fCharCount
> 0) {
484 ((ParagraphLayout
*) this)->computeMetrics();
490 ParagraphLayout::Line
*ParagraphLayout::nextLine(float width
)
492 if (fLineEnd
>= fCharCount
) {
496 fLineStart
= fLineEnd
;
499 le_int32 glyph
= fCharToMinGlyphMap
[fLineStart
];
500 float widthSoFar
= 0;
502 while (glyph
< fGlyphCount
&& widthSoFar
+ fGlyphWidths
[glyph
] <= width
) {
503 widthSoFar
+= fGlyphWidths
[glyph
++];
506 // If no glyphs fit on the line, force one to fit.
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
) {
517 fLineEnd
= previousBreak(fGlyphToCharMap
[glyph
]);
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
++];
526 fLineEnd
= fCharCount
;
529 return computeVisualRuns();
532 void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel
)
534 UErrorCode bidiStatus
= U_ZERO_ERROR
;
536 if (fLevelRuns
!= NULL
) {
540 fEmbeddingLevels
= LE_NEW_ARRAY(UBiDiLevel
, fCharCount
);
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
);
546 while (ch
< runLimit
) {
547 fEmbeddingLevels
[ch
++] = runLevel
;
552 fParaBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
553 ubidi_setPara(fParaBidi
, fChars
, fCharCount
, paragraphLevel
, fEmbeddingLevels
, &bidiStatus
);
555 if (fLevelRuns
== NULL
) {
556 le_int32 levelRunCount
= ubidi_countRuns(fParaBidi
, &bidiStatus
);
557 ValueRuns
*levelRuns
= new ValueRuns(levelRunCount
);
559 le_int32 logicalStart
= 0;
564 for (run
= 0; run
< levelRunCount
; run
+= 1) {
565 ubidi_getLogicalRun(fParaBidi
, logicalStart
, &limit
, &level
);
566 levelRuns
->add(level
, limit
);
567 logicalStart
= limit
;
570 fLevelRuns
= levelRuns
;
571 fClientLevels
= FALSE
;
575 void ParagraphLayout::computeScripts()
577 UErrorCode scriptStatus
= U_ZERO_ERROR
;
578 UScriptRun
*sr
= uscript_openRun(fChars
, fCharCount
, &scriptStatus
);
579 ValueRuns
*scriptRuns
= new ValueRuns(0);
583 while (uscript_nextRun(sr
, NULL
, &limit
, &script
)) {
584 scriptRuns
->add(script
, limit
);
587 uscript_closeRun(sr
);
589 fScriptRuns
= scriptRuns
;
590 fClientScripts
= FALSE
;
593 void ParagraphLayout::computeLocales()
595 LocaleRuns
*localeRuns
= new LocaleRuns(0);
596 const Locale
*defaultLocale
= &Locale::getDefault();
598 localeRuns
->add(defaultLocale
, fCharCount
);
600 fLocaleRuns
= localeRuns
;
601 fClientLocales
= FALSE
;
604 void ParagraphLayout::computeSubFonts(const FontRuns
*fontRuns
, LEErrorCode
&status
)
606 if (LE_FAILURE(status
)) {
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
;
619 styleRuns
.getRuns(styleRunLimits
, styleIndices
);
624 for (run
= 0; run
< styleRunCount
; run
+= 1) {
625 const LEFontInstance
*runFont
= fontRuns
->getFont(si
[0]);
626 le_int32 script
= fScriptRuns
->getValue(si
[1]);
628 while (offset
< styleRunLimits
[run
]) {
629 const LEFontInstance
*subFont
= runFont
->getSubFont(fChars
, &offset
, styleRunLimits
[run
], script
, status
);
631 if (LE_FAILURE(status
)) {
636 subFontRuns
->add(subFont
, offset
);
642 fFontRuns
= subFontRuns
;
645 LE_DELETE_ARRAY(styleIndices
);
646 LE_DELETE_ARRAY(styleRunLimits
);
649 void ParagraphLayout::computeMetrics()
651 le_int32 i
, count
= fFontRuns
->getCount();
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
;
661 if (ascent
> fAscent
) {
665 if (descent
> fDescent
) {
669 if (leading
> fLeading
) {
678 fLeading
= maxDL
- fDescent
;
684 const char *localeCode
;
685 le_int32 languageCode
;
688 static const LanguageMap languageMap
[] =
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
725 static const le_int32 languageMapCount
= ARRAY_SIZE(languageMap
);
727 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
729 char code
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
730 const char *language
= locale
->getISO3Language();
731 const char *country
= locale
->getISO3Country();
733 uprv_strcat(code
, language
);
735 if ((uprv_strcmp(language
, "zho") == 0) && country
!= NULL
) {
736 uprv_strcat(code
, "_");
737 uprv_strcat(code
, country
);
740 for (le_int32 i
= 0; i
< languageMapCount
; i
+= 1) {
741 if (uprv_strcmp(code
, languageMap
[i
].localeCode
) == 0) {
742 return languageMap
[i
].languageCode
;
746 return nullLanguageCode
;
750 // TODO - dummy implementation for right now...
751 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
753 return nullLanguageCode
;
757 le_bool
ParagraphLayout::isComplex(UScriptCode script
)
759 if (script
< 0 || script
>= USCRIPT_CODE_LIMIT
) {
763 return complexTable
[script
];
766 le_int32
ParagraphLayout::previousBreak(le_int32 charIndex
)
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
]))) {
776 // Create the BreakIterator if we don't already have one
777 if (fBreakIterator
== NULL
) {
779 UCharCharacterIterator
*iter
= new UCharCharacterIterator(fChars
, fCharCount
);
780 UErrorCode status
= U_ZERO_ERROR
;
782 fBreakIterator
= BreakIterator::createLineInstance(thai
, status
);
783 fBreakIterator
->adoptText(iter
);
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
790 return fBreakIterator
->preceding(charIndex
+ 1);
793 ParagraphLayout::Line
*ParagraphLayout::computeVisualRuns()
795 UErrorCode bidiStatus
= U_ZERO_ERROR
;
796 le_int32 dirRunCount
, visualRun
;
800 fFirstVisualRun
= getCharRun(fLineStart
);
801 fLastVisualRun
= getCharRun(fLineEnd
- 1);
803 if (fLineBidi
== NULL
) {
804 fLineBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
807 ubidi_setLine(fParaBidi
, fLineStart
, fLineEnd
, fLineBidi
, &bidiStatus
);
808 dirRunCount
= ubidi_countRuns(fLineBidi
, &bidiStatus
);
810 Line
*line
= new Line();
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;
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;
827 appendRun(line
, run
, firstChar
, lastChar
);
834 void ParagraphLayout::appendRun(ParagraphLayout::Line
*line
, le_int32 run
, le_int32 firstChar
, le_int32 lastChar
)
836 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
837 le_int32 inGlyph
, outGlyph
;
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;
846 for (ch
= firstChar
; ch
<= lastChar
; ch
+= 1) {
847 le_int32 minGlyph
= fCharToMinGlyphMap
[ch
];
848 le_int32 maxGlyph
= fCharToMaxGlyphMap
[ch
];
850 if (minGlyph
< leftGlyph
) {
851 leftGlyph
= minGlyph
;
854 if (maxGlyph
> rightGlyph
) {
855 rightGlyph
= maxGlyph
;
859 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
860 le_int32 swap
= rightGlyph
;
861 le_int32 last
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
863 // Here, we want to remove the glyphBase bias...
864 rightGlyph
= last
- leftGlyph
;
865 leftGlyph
= last
- swap
;
867 rightGlyph
-= glyphBase
;
868 leftGlyph
-= glyphBase
;
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
877 fVisualRunLastX
-= fStyleRunInfo
[run
].positions
[leftGlyph
* 2];
879 // Make rightGlyph be the glyph just to the right of
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
);
889 LE_ARRAY_COPY(glyphs
, &fStyleRunInfo
[run
].glyphs
[leftGlyph
], glyphCount
);
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 */;
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];
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
];
906 for (outGlyph
= 0, inGlyph
= rightGlyph
- 1; inGlyph
>= leftGlyph
; inGlyph
-= 1, outGlyph
+= 1) {
907 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[glyphBase
+ inGlyph
];
911 line
->append(fStyleRunInfo
[run
].font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
914 le_int32
ParagraphLayout::getCharRun(le_int32 charIndex
)
916 if (charIndex
< 0 || charIndex
> fCharCount
) {
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.
926 while (charIndex
>= fStyleRunLimits
[run
]) {
934 const char ParagraphLayout::Line::fgClassID
= 0;
936 #define INITIAL_RUN_CAPACITY 4
937 #define RUN_CAPACITY_GROW_LIMIT 16
939 ParagraphLayout::Line::~Line()
943 for (i
= 0; i
< fRunCount
; i
+= 1) {
947 LE_DELETE_ARRAY(fRuns
);
950 le_int32
ParagraphLayout::Line::getAscent() const
953 ((ParagraphLayout::Line
*)this)->computeMetrics();
959 le_int32
ParagraphLayout::Line::getDescent() const
962 ((ParagraphLayout::Line
*)this)->computeMetrics();
968 le_int32
ParagraphLayout::Line::getLeading() const
971 ((ParagraphLayout::Line
*)this)->computeMetrics();
977 le_int32
ParagraphLayout::Line::getWidth() const
979 const VisualRun
*lastRun
= getVisualRun(fRunCount
- 1);
980 le_int32 glyphCount
= lastRun
->getGlyphCount();
981 const float *positions
= lastRun
->getPositions();
983 return (le_int32
) positions
[glyphCount
* 2];
986 const ParagraphLayout::VisualRun
*ParagraphLayout::Line::getVisualRun(le_int32 runIndex
) const
988 if (runIndex
< 0 || runIndex
>= fRunCount
) {
992 return fRuns
[runIndex
];
995 void ParagraphLayout::Line::append(const LEFontInstance
*font
, UBiDiDirection direction
, le_int32 glyphCount
,
996 const LEGlyphID glyphs
[], const float positions
[], const le_int32 glyphToCharMap
[])
998 if (fRunCount
>= fRunCapacity
) {
999 if (fRunCapacity
== 0) {
1000 fRunCapacity
= INITIAL_RUN_CAPACITY
;
1001 fRuns
= LE_NEW_ARRAY(ParagraphLayout::VisualRun
*, fRunCapacity
);
1003 fRunCapacity
+= (fRunCapacity
< RUN_CAPACITY_GROW_LIMIT
? fRunCapacity
: RUN_CAPACITY_GROW_LIMIT
);
1004 fRuns
= (ParagraphLayout::VisualRun
**) LE_GROW_ARRAY(fRuns
, fRunCapacity
);
1008 fRuns
[fRunCount
++] = new ParagraphLayout::VisualRun(font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1011 void ParagraphLayout::Line::computeMetrics()
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
;
1021 if (ascent
> fAscent
) {
1025 if (descent
> fDescent
) {
1029 if (leading
> fLeading
) {
1038 fLeading
= maxDL
- fDescent
;
1041 const char ParagraphLayout::VisualRun::fgClassID
= 0;
1043 ParagraphLayout::VisualRun::~VisualRun()
1045 LE_DELETE_ARRAY(fGlyphToCharMap
);
1046 LE_DELETE_ARRAY(fPositions
);
1047 LE_DELETE_ARRAY(fGlyphs
);