2 **********************************************************************
3 * Copyright (C) 2002-2006, 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])
36 StyleRuns(const RunArray
*styleRunArrays
[], le_int32 styleCount
);
40 le_int32
getRuns(le_int32 runLimits
[], le_int32 styleIndices
[]);
47 le_int32
*fStyleIndices
;
50 StyleRuns::StyleRuns(const RunArray
*styleRunArrays
[], le_int32 styleCount
)
51 : fStyleCount(styleCount
), fRunCount(0), fRunLimits(NULL
), fStyleIndices(NULL
)
53 le_int32 maxRunCount
= 0;
54 le_int32 style
, run
, runStyle
;
55 le_int32
*currentRun
= LE_NEW_ARRAY(le_int32
, styleCount
);
57 for (int i
= 0; i
< styleCount
; i
+= 1) {
58 maxRunCount
+= styleRunArrays
[i
]->getCount();
61 maxRunCount
-= styleCount
- 1;
63 fRunLimits
= LE_NEW_ARRAY(le_int32
, maxRunCount
);
64 fStyleIndices
= LE_NEW_ARRAY(le_int32
, maxRunCount
* styleCount
);
66 for (style
= 0; style
< styleCount
; style
+= 1) {
67 currentRun
[style
] = 0;
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.
79 while (currentRun
[0] < styleRunArrays
[0]->getCount()) {
80 fRunLimits
[run
] = 0x7FFFFFFF;
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
]);
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
];
93 if (styleRunArrays
[style
]->getLimit(currentRun
[style
]) == fRunLimits
[run
]) {
94 currentRun
[style
] += 1;
102 LE_DELETE_ARRAY(currentRun
);
105 StyleRuns::~StyleRuns()
109 LE_DELETE_ARRAY(fStyleIndices
);
110 fStyleIndices
= NULL
;
112 LE_DELETE_ARRAY(fRunLimits
);
116 le_int32
StyleRuns::getRuns(le_int32 runLimits
[], le_int32 styleIndices
[])
118 if (runLimits
!= NULL
) {
119 LE_ARRAY_COPY(runLimits
, fRunLimits
, fRunCount
);
122 if (styleIndices
!= NULL
) {
123 LE_ARRAY_COPY(styleIndices
, fStyleIndices
, fRunCount
* fStyleCount
);
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.
135 static const le_bool complexTable
[scriptCodeCount
] = {
143 FALSE
, /* Copt=Qaac */
243 const char ParagraphLayout::fgClassID
= 0;
246 * How to deal with composite fonts:
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.
253 * An optimization would be to only do this if there's at least one composite font...
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...
260 * * Take the LineInfo and LineRun types from Paragraph and use them here, incorporate them into the API.
262 * * Might want to change the name of the StyleRun type, and make a new one that holds fonts, scripts and levels?
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
,
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)
284 if (LE_FAILURE(status
)) {
289 // FIXME: should check the limit arrays for consistency...
291 computeLevels(paragraphLevel
);
293 if (scriptRuns
== NULL
) {
297 if (localeRuns
== NULL
) {
301 computeSubFonts(fontRuns
, status
);
303 if (LE_FAILURE(status
)) {
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
;
315 fStyleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
317 fStyleRunLimits
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
);
318 fStyleIndices
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
* styleCount
);
320 styleRuns
.getRuns(fStyleRunLimits
, fStyleIndices
);
322 // now build a LayoutEngine for each style run...
323 le_int32
*styleIndices
= fStyleIndices
;
324 le_int32 run
, runStart
;
326 fStyleRunInfo
= LE_NEW_ARRAY(StyleRunInfo
, fStyleRunCount
);
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
;
338 fStyleRunInfo
[run
].engine
= LayoutEngine::layoutEngineFactory(fStyleRunInfo
[run
].font
,
339 fStyleRunInfo
[run
].script
, getLanguageCode(fStyleRunInfo
[run
].locale
), layoutStatus
);
341 fStyleRunInfo
[run
].glyphCount
= fStyleRunInfo
[run
].engine
->layoutChars(fChars
, runStart
, fStyleRunLimits
[run
] - runStart
, fCharCount
,
342 fStyleRunInfo
[run
].level
& 1, 0, 0, layoutStatus
);
344 runStart
= fStyleRunLimits
[run
];
345 styleIndices
+= styleCount
;
346 fGlyphCount
+= fStyleRunInfo
[run
].glyphCount
;
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.)
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);
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
;
368 fStyleRunInfo
[run
].glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
369 fStyleRunInfo
[run
].positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
371 engine
->getGlyphs(fStyleRunInfo
[run
].glyphs
, layoutStatus
);
372 engine
->getGlyphPositions(fStyleRunInfo
[run
].positions
, layoutStatus
);
373 engine
->getCharIndices(&fGlyphToCharMap
[glyphBase
], runStart
, layoutStatus
);
375 for (glyph
= 0; glyph
< glyphCount
; glyph
+= 1) {
376 fGlyphWidths
[glyphBase
+ glyph
] = fStyleRunInfo
[run
].positions
[glyph
* 2 + 2] - fStyleRunInfo
[run
].positions
[glyph
* 2];
379 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
380 LXUtilities::reverse(&fGlyphWidths
[glyphBase
], glyphCount
);
381 LXUtilities::reverse(&fGlyphToCharMap
[glyphBase
], glyphCount
);
384 runStart
= fStyleRunLimits
[run
];
387 fStyleRunInfo
[run
].engine
= NULL
;
390 fGlyphToCharMap
[fGlyphCount
] = fCharCount
;
392 for (glyph
= fGlyphCount
- 1; glyph
>= 0; glyph
-= 1) {
393 le_int32 ch
= fGlyphToCharMap
[glyph
];
395 fCharToMinGlyphMap
[ch
] = glyph
;
398 fCharToMinGlyphMap
[fCharCount
] = fGlyphCount
;
400 for (glyph
= 0; glyph
< fGlyphCount
; glyph
+= 1) {
401 le_int32 ch
= fGlyphToCharMap
[glyph
];
403 fCharToMaxGlyphMap
[ch
] = glyph
;
406 fCharToMaxGlyphMap
[fCharCount
] = fGlyphCount
;
409 ParagraphLayout::~ParagraphLayout()
411 delete (FontRuns
*) fFontRuns
;
413 if (! fClientLevels
) {
414 delete (ValueRuns
*) fLevelRuns
;
417 fClientLevels
= TRUE
;
420 if (! fClientScripts
) {
421 delete (ValueRuns
*) fScriptRuns
;
424 fClientScripts
= TRUE
;
427 if (! fClientLocales
) {
428 delete (LocaleRuns
*) fLocaleRuns
;
431 fClientLocales
= TRUE
;
434 if (fEmbeddingLevels
!= NULL
) {
435 LE_DELETE_ARRAY(fEmbeddingLevels
);
436 fEmbeddingLevels
= NULL
;
439 if (fGlyphToCharMap
!= NULL
) {
440 LE_DELETE_ARRAY(fGlyphToCharMap
);
441 fGlyphToCharMap
= NULL
;
444 if (fCharToMinGlyphMap
!= NULL
) {
445 LE_DELETE_ARRAY(fCharToMinGlyphMap
);
446 fCharToMinGlyphMap
= NULL
;
449 if (fCharToMaxGlyphMap
!= NULL
) {
450 LE_DELETE_ARRAY(fCharToMaxGlyphMap
);
451 fCharToMaxGlyphMap
= NULL
;
454 if (fGlyphWidths
!= NULL
) {
455 LE_DELETE_ARRAY(fGlyphWidths
);
459 if (fParaBidi
!= NULL
) {
460 ubidi_close(fParaBidi
);
464 if (fLineBidi
!= NULL
) {
465 ubidi_close(fLineBidi
);
469 if (fStyleRunCount
> 0) {
472 LE_DELETE_ARRAY(fStyleRunLimits
);
473 LE_DELETE_ARRAY(fStyleIndices
);
475 for (run
= 0; run
< fStyleRunCount
; run
+= 1) {
476 LE_DELETE_ARRAY(fStyleRunInfo
[run
].glyphs
);
477 LE_DELETE_ARRAY(fStyleRunInfo
[run
].positions
);
479 fStyleRunInfo
[run
].glyphs
= NULL
;
480 fStyleRunInfo
[run
].positions
= NULL
;
483 LE_DELETE_ARRAY(fStyleRunInfo
);
485 fStyleRunLimits
= NULL
;
486 fStyleIndices
= NULL
;
487 fStyleRunInfo
= NULL
;
491 if (fBreakIterator
!= NULL
) {
492 delete fBreakIterator
;
493 fBreakIterator
= NULL
;
498 le_bool
ParagraphLayout::isComplex(const LEUnicode chars
[], le_int32 count
)
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
;
505 while (uscript_nextRun(sr
, NULL
, NULL
, &scriptCode
)) {
506 if (isComplex(scriptCode
)) {
512 uscript_closeRun(sr
);
516 le_int32
ParagraphLayout::getAscent() const
518 if (fAscent
<= 0 && fCharCount
> 0) {
519 ((ParagraphLayout
*) this)->computeMetrics();
525 le_int32
ParagraphLayout::getDescent() const
527 if (fAscent
<= 0 && fCharCount
> 0) {
528 ((ParagraphLayout
*) this)->computeMetrics();
534 le_int32
ParagraphLayout::getLeading() const
536 if (fAscent
<= 0 && fCharCount
> 0) {
537 ((ParagraphLayout
*) this)->computeMetrics();
543 ParagraphLayout::Line
*ParagraphLayout::nextLine(float width
)
545 if (fLineEnd
>= fCharCount
) {
549 fLineStart
= fLineEnd
;
552 le_int32 glyph
= fCharToMinGlyphMap
[fLineStart
];
553 float widthSoFar
= 0;
555 while (glyph
< fGlyphCount
&& widthSoFar
+ fGlyphWidths
[glyph
] <= width
) {
556 widthSoFar
+= fGlyphWidths
[glyph
++];
559 // If no glyphs fit on the line, force one to fit.
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
) {
570 fLineEnd
= previousBreak(fGlyphToCharMap
[glyph
]);
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
++];
579 fLineEnd
= fCharCount
;
582 return computeVisualRuns();
585 void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel
)
587 UErrorCode bidiStatus
= U_ZERO_ERROR
;
589 if (fLevelRuns
!= NULL
) {
593 fEmbeddingLevels
= LE_NEW_ARRAY(UBiDiLevel
, fCharCount
);
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
);
599 while (ch
< runLimit
) {
600 fEmbeddingLevels
[ch
++] = runLevel
;
605 fParaBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
606 ubidi_setPara(fParaBidi
, fChars
, fCharCount
, paragraphLevel
, fEmbeddingLevels
, &bidiStatus
);
608 if (fLevelRuns
== NULL
) {
609 le_int32 levelRunCount
= ubidi_countRuns(fParaBidi
, &bidiStatus
);
610 ValueRuns
*levelRuns
= new ValueRuns(levelRunCount
);
612 le_int32 logicalStart
= 0;
617 for (run
= 0; run
< levelRunCount
; run
+= 1) {
618 ubidi_getLogicalRun(fParaBidi
, logicalStart
, &limit
, &level
);
619 levelRuns
->add(level
, limit
);
620 logicalStart
= limit
;
623 fLevelRuns
= levelRuns
;
624 fClientLevels
= FALSE
;
628 void ParagraphLayout::computeScripts()
630 UErrorCode scriptStatus
= U_ZERO_ERROR
;
631 UScriptRun
*sr
= uscript_openRun(fChars
, fCharCount
, &scriptStatus
);
632 ValueRuns
*scriptRuns
= new ValueRuns(0);
636 while (uscript_nextRun(sr
, NULL
, &limit
, &script
)) {
637 scriptRuns
->add(script
, limit
);
640 uscript_closeRun(sr
);
642 fScriptRuns
= scriptRuns
;
643 fClientScripts
= FALSE
;
646 void ParagraphLayout::computeLocales()
648 LocaleRuns
*localeRuns
= new LocaleRuns(0);
649 const Locale
*defaultLocale
= &Locale::getDefault();
651 localeRuns
->add(defaultLocale
, fCharCount
);
653 fLocaleRuns
= localeRuns
;
654 fClientLocales
= FALSE
;
657 void ParagraphLayout::computeSubFonts(const FontRuns
*fontRuns
, LEErrorCode
&status
)
659 if (LE_FAILURE(status
)) {
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
;
672 styleRuns
.getRuns(styleRunLimits
, styleIndices
);
677 for (run
= 0; run
< styleRunCount
; run
+= 1) {
678 const LEFontInstance
*runFont
= fontRuns
->getFont(si
[0]);
679 le_int32 script
= fScriptRuns
->getValue(si
[1]);
681 while (offset
< styleRunLimits
[run
]) {
682 const LEFontInstance
*subFont
= runFont
->getSubFont(fChars
, &offset
, styleRunLimits
[run
], script
, status
);
684 if (LE_FAILURE(status
)) {
689 subFontRuns
->add(subFont
, offset
);
695 fFontRuns
= subFontRuns
;
698 LE_DELETE_ARRAY(styleIndices
);
699 LE_DELETE_ARRAY(styleRunLimits
);
702 void ParagraphLayout::computeMetrics()
704 le_int32 i
, count
= fFontRuns
->getCount();
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
;
714 if (ascent
> fAscent
) {
718 if (descent
> fDescent
) {
722 if (leading
> fLeading
) {
731 fLeading
= maxDL
- fDescent
;
737 const char *localeCode
;
738 le_int32 languageCode
;
741 static const LanguageMap languageMap
[] =
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
778 static const le_int32 languageMapCount
= ARRAY_SIZE(languageMap
);
780 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
782 char code
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
783 const char *language
= locale
->getISO3Language();
784 const char *country
= locale
->getISO3Country();
786 uprv_strcat(code
, language
);
788 if ((uprv_strcmp(language
, "zho") == 0) && country
!= NULL
) {
789 uprv_strcat(code
, "_");
790 uprv_strcat(code
, country
);
793 for (le_int32 i
= 0; i
< languageMapCount
; i
+= 1) {
794 if (uprv_strcmp(code
, languageMap
[i
].localeCode
) == 0) {
795 return languageMap
[i
].languageCode
;
799 return nullLanguageCode
;
803 // TODO - dummy implementation for right now...
804 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
806 return nullLanguageCode
;
810 le_bool
ParagraphLayout::isComplex(UScriptCode script
)
812 if (script
< 0 || script
>= USCRIPT_CODE_LIMIT
) {
816 return complexTable
[script
];
819 le_int32
ParagraphLayout::previousBreak(le_int32 charIndex
)
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
]))) {
829 // Create the BreakIterator if we don't already have one
830 if (fBreakIterator
== NULL
) {
832 UCharCharacterIterator
*iter
= new UCharCharacterIterator(fChars
, fCharCount
);
833 UErrorCode status
= U_ZERO_ERROR
;
835 fBreakIterator
= BreakIterator::createLineInstance(thai
, status
);
836 fBreakIterator
->adoptText(iter
);
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
843 return fBreakIterator
->preceding(charIndex
+ 1);
846 ParagraphLayout::Line
*ParagraphLayout::computeVisualRuns()
848 UErrorCode bidiStatus
= U_ZERO_ERROR
;
849 le_int32 dirRunCount
, visualRun
;
853 fFirstVisualRun
= getCharRun(fLineStart
);
854 fLastVisualRun
= getCharRun(fLineEnd
- 1);
856 if (fLineBidi
== NULL
) {
857 fLineBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
860 ubidi_setLine(fParaBidi
, fLineStart
, fLineEnd
, fLineBidi
, &bidiStatus
);
861 dirRunCount
= ubidi_countRuns(fLineBidi
, &bidiStatus
);
863 Line
*line
= new Line();
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;
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;
880 appendRun(line
, run
, firstChar
, lastChar
);
887 void ParagraphLayout::appendRun(ParagraphLayout::Line
*line
, le_int32 run
, le_int32 firstChar
, le_int32 lastChar
)
889 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
890 le_int32 inGlyph
, outGlyph
;
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;
899 for (ch
= firstChar
; ch
<= lastChar
; ch
+= 1) {
900 le_int32 minGlyph
= fCharToMinGlyphMap
[ch
];
901 le_int32 maxGlyph
= fCharToMaxGlyphMap
[ch
];
903 if (minGlyph
< leftGlyph
) {
904 leftGlyph
= minGlyph
;
907 if (maxGlyph
> rightGlyph
) {
908 rightGlyph
= maxGlyph
;
912 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
913 le_int32 swap
= rightGlyph
;
914 le_int32 last
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
916 // Here, we want to remove the glyphBase bias...
917 rightGlyph
= last
- leftGlyph
;
918 leftGlyph
= last
- swap
;
920 rightGlyph
-= glyphBase
;
921 leftGlyph
-= glyphBase
;
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
930 fVisualRunLastX
-= fStyleRunInfo
[run
].positions
[leftGlyph
* 2];
932 // Make rightGlyph be the glyph just to the right of
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
);
942 LE_ARRAY_COPY(glyphs
, &fStyleRunInfo
[run
].glyphs
[leftGlyph
], glyphCount
);
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 */;
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];
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
];
959 for (outGlyph
= 0, inGlyph
= rightGlyph
- 1; inGlyph
>= leftGlyph
; inGlyph
-= 1, outGlyph
+= 1) {
960 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[glyphBase
+ inGlyph
];
964 line
->append(fStyleRunInfo
[run
].font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
967 le_int32
ParagraphLayout::getCharRun(le_int32 charIndex
)
969 if (charIndex
< 0 || charIndex
> fCharCount
) {
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.
979 while (charIndex
>= fStyleRunLimits
[run
]) {
987 const char ParagraphLayout::Line::fgClassID
= 0;
989 #define INITIAL_RUN_CAPACITY 4
990 #define RUN_CAPACITY_GROW_LIMIT 16
992 ParagraphLayout::Line::~Line()
996 for (i
= 0; i
< fRunCount
; i
+= 1) {
1000 LE_DELETE_ARRAY(fRuns
);
1003 le_int32
ParagraphLayout::Line::getAscent() const
1006 ((ParagraphLayout::Line
*)this)->computeMetrics();
1012 le_int32
ParagraphLayout::Line::getDescent() const
1015 ((ParagraphLayout::Line
*)this)->computeMetrics();
1021 le_int32
ParagraphLayout::Line::getLeading() const
1024 ((ParagraphLayout::Line
*)this)->computeMetrics();
1030 le_int32
ParagraphLayout::Line::getWidth() const
1032 const VisualRun
*lastRun
= getVisualRun(fRunCount
- 1);
1033 le_int32 glyphCount
= lastRun
->getGlyphCount();
1034 const float *positions
= lastRun
->getPositions();
1036 return (le_int32
) positions
[glyphCount
* 2];
1039 const ParagraphLayout::VisualRun
*ParagraphLayout::Line::getVisualRun(le_int32 runIndex
) const
1041 if (runIndex
< 0 || runIndex
>= fRunCount
) {
1045 return fRuns
[runIndex
];
1048 void ParagraphLayout::Line::append(const LEFontInstance
*font
, UBiDiDirection direction
, le_int32 glyphCount
,
1049 const LEGlyphID glyphs
[], const float positions
[], const le_int32 glyphToCharMap
[])
1051 if (fRunCount
>= fRunCapacity
) {
1052 if (fRunCapacity
== 0) {
1053 fRunCapacity
= INITIAL_RUN_CAPACITY
;
1054 fRuns
= LE_NEW_ARRAY(ParagraphLayout::VisualRun
*, fRunCapacity
);
1056 fRunCapacity
+= (fRunCapacity
< RUN_CAPACITY_GROW_LIMIT
? fRunCapacity
: RUN_CAPACITY_GROW_LIMIT
);
1057 fRuns
= (ParagraphLayout::VisualRun
**) LE_GROW_ARRAY(fRuns
, fRunCapacity
);
1061 fRuns
[fRunCount
++] = new ParagraphLayout::VisualRun(font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1064 void ParagraphLayout::Line::computeMetrics()
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
;
1074 if (ascent
> fAscent
) {
1078 if (descent
> fDescent
) {
1082 if (leading
> fLeading
) {
1091 fLeading
= maxDL
- fDescent
;
1094 const char ParagraphLayout::VisualRun::fgClassID
= 0;
1096 ParagraphLayout::VisualRun::~VisualRun()
1098 LE_DELETE_ARRAY(fGlyphToCharMap
);
1099 LE_DELETE_ARRAY(fPositions
);
1100 LE_DELETE_ARRAY(fGlyphs
);