2 **********************************************************************
3 * Copyright (C) 2002-2010, 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 */
276 const char ParagraphLayout::fgClassID
= 0;
278 static void fillMissingCharToGlyphMapValues(le_int32
*charToGlyphMap
,
279 le_int32 charCount
) {
280 le_int32 lastValidGlyph
= -1;
282 for (ch
= 0; ch
<= charCount
; ch
+= 1) {
283 if (charToGlyphMap
[ch
] == -1) {
284 charToGlyphMap
[ch
] = lastValidGlyph
;
286 lastValidGlyph
= charToGlyphMap
[ch
];
292 * How to deal with composite fonts:
294 * Don't store the client's FontRuns; we'll need to compute sub-font FontRuns using Doug's
295 * LEFontInstance method. Do that by intersecting the client's FontRuns with fScriptRuns. Use
296 * that to compute fFontRuns, and then intersect fFontRuns, fScriptRuns and fLevelRuns. Doing
297 * it in this order means we do a two-way intersection and a three-way intersection.
299 * An optimization would be to only do this if there's at least one composite font...
303 * * Return the sub-fonts as the run fonts... could keep the mapping back to the client's FontRuns
304 * but that probably makes it more complicated of everyone...
306 * * Take the LineInfo and LineRun types from Paragraph and use them here, incorporate them into the API.
308 * * Might want to change the name of the StyleRun type, and make a new one that holds fonts, scripts and levels?
311 ParagraphLayout::ParagraphLayout(const LEUnicode chars
[], le_int32 count
,
312 const FontRuns
*fontRuns
,
313 const ValueRuns
*levelRuns
,
314 const ValueRuns
*scriptRuns
,
315 const LocaleRuns
*localeRuns
,
316 UBiDiLevel paragraphLevel
, le_bool vertical
,
318 : fChars(chars
), fCharCount(count
),
319 fFontRuns(NULL
), fLevelRuns(levelRuns
), fScriptRuns(scriptRuns
), fLocaleRuns(localeRuns
),
320 fVertical(vertical
), fClientLevels(TRUE
), fClientScripts(TRUE
), fClientLocales(TRUE
), fEmbeddingLevels(NULL
),
321 fAscent(0), fDescent(0), fLeading(0),
322 fGlyphToCharMap(NULL
), fCharToMinGlyphMap(NULL
), fCharToMaxGlyphMap(NULL
), fGlyphWidths(NULL
), fGlyphCount(0),
323 fParaBidi(NULL
), fLineBidi(NULL
),
324 fStyleRunLimits(NULL
), fStyleIndices(NULL
), fStyleRunCount(0),
325 fBreakIterator(NULL
), fLineStart(-1), fLineEnd(0),
326 /*fVisualRuns(NULL), fStyleRunInfo(NULL), fVisualRunCount(-1),
327 fFirstVisualRun(-1), fLastVisualRun(-1),*/ fVisualRunLastX(0), fVisualRunLastY(0)
330 if (LE_FAILURE(status
)) {
335 // FIXME: should check the limit arrays for consistency...
337 computeLevels(paragraphLevel
);
339 if (scriptRuns
== NULL
) {
343 if (localeRuns
== NULL
) {
347 computeSubFonts(fontRuns
, status
);
349 if (LE_FAILURE(status
)) {
355 // now intersect the font, direction and script runs...
356 const RunArray
*styleRunArrays
[] = {fFontRuns
, fLevelRuns
, fScriptRuns
, fLocaleRuns
};
357 le_int32 styleCount
= sizeof styleRunArrays
/ sizeof styleRunArrays
[0];
358 StyleRuns
styleRuns(styleRunArrays
, styleCount
);
359 LEErrorCode layoutStatus
= LE_NO_ERROR
;
361 fStyleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
363 fStyleRunLimits
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
);
364 fStyleIndices
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
* styleCount
);
365 if ((fStyleRunLimits
== NULL
) || (fStyleIndices
== NULL
)) {
366 status
= LE_MEMORY_ALLOCATION_ERROR
;
370 styleRuns
.getRuns(fStyleRunLimits
, fStyleIndices
);
372 // now build a LayoutEngine for each style run...
373 le_int32
*styleIndices
= fStyleIndices
;
374 le_int32 run
, runStart
;
376 fStyleRunInfo
= LE_NEW_ARRAY(StyleRunInfo
, fStyleRunCount
);
377 if (fStyleRunInfo
== NULL
) {
378 status
= LE_MEMORY_ALLOCATION_ERROR
;
383 for (runStart
= 0, run
= 0; run
< fStyleRunCount
; run
+= 1) {
384 fStyleRunInfo
[run
].font
= NULL
;
385 fStyleRunInfo
[run
].runBase
= 0;
386 fStyleRunInfo
[run
].runLimit
= 0;
387 fStyleRunInfo
[run
].script
= (UScriptCode
)0;
388 fStyleRunInfo
[run
].locale
= NULL
;
389 fStyleRunInfo
[run
].level
= 0;
390 fStyleRunInfo
[run
].glyphBase
= 0;
391 fStyleRunInfo
[run
].engine
= NULL
;
392 fStyleRunInfo
[run
].glyphCount
= 0;
393 fStyleRunInfo
[run
].glyphs
= NULL
;
394 fStyleRunInfo
[run
].positions
= NULL
;
399 for (runStart
= 0, run
= 0; run
< fStyleRunCount
; run
+= 1) {
400 fStyleRunInfo
[run
].font
= fFontRuns
->getFont(styleIndices
[0]);
401 fStyleRunInfo
[run
].runBase
= runStart
;
402 fStyleRunInfo
[run
].runLimit
= fStyleRunLimits
[run
];
403 fStyleRunInfo
[run
].script
= (UScriptCode
) fScriptRuns
->getValue(styleIndices
[2]);
404 fStyleRunInfo
[run
].locale
= fLocaleRuns
->getLocale(styleIndices
[3]);
405 fStyleRunInfo
[run
].level
= (UBiDiLevel
) fLevelRuns
->getValue(styleIndices
[1]);
406 fStyleRunInfo
[run
].glyphBase
= fGlyphCount
;
408 fStyleRunInfo
[run
].engine
= LayoutEngine::layoutEngineFactory(fStyleRunInfo
[run
].font
,
409 fStyleRunInfo
[run
].script
, getLanguageCode(fStyleRunInfo
[run
].locale
), layoutStatus
);
410 if (LE_FAILURE(layoutStatus
)) {
411 status
= layoutStatus
;
415 fStyleRunInfo
[run
].glyphCount
= fStyleRunInfo
[run
].engine
->layoutChars(fChars
, runStart
, fStyleRunLimits
[run
] - runStart
, fCharCount
,
416 fStyleRunInfo
[run
].level
& 1, 0, 0, layoutStatus
);
417 if (LE_FAILURE(layoutStatus
)) {
418 status
= layoutStatus
;
422 runStart
= fStyleRunLimits
[run
];
423 styleIndices
+= styleCount
;
424 fGlyphCount
+= fStyleRunInfo
[run
].glyphCount
;
427 // Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps,
428 // in logical order. (Both maps need an extra entry for the end of the text.)
430 // For each layout get the positions and convert them into glyph widths, in
431 // logical order. Get the glyph-to-char mapping, offset by starting index in the
432 // character array. Swap the glyph width and glyph-to-char arrays into logical order.
433 // Finally, fill in the char-to-glyph mappings.
434 fGlyphWidths
= LE_NEW_ARRAY(float, fGlyphCount
);
435 fGlyphToCharMap
= LE_NEW_ARRAY(le_int32
, fGlyphCount
+ 1);
436 fCharToMinGlyphMap
= LE_NEW_ARRAY(le_int32
, fCharCount
+ 1);
437 fCharToMaxGlyphMap
= LE_NEW_ARRAY(le_int32
, fCharCount
+ 1);
438 if ((fGlyphWidths
== NULL
) || (fGlyphToCharMap
== NULL
) ||
439 (fCharToMinGlyphMap
== NULL
) || (fCharToMaxGlyphMap
== NULL
)) {
440 status
= LE_MEMORY_ALLOCATION_ERROR
;
446 for (runStart
= 0, run
= 0; run
< fStyleRunCount
; run
+= 1) {
447 LayoutEngine
*engine
= fStyleRunInfo
[run
].engine
;
448 le_int32 glyphCount
= fStyleRunInfo
[run
].glyphCount
;
449 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
451 fStyleRunInfo
[run
].glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
452 fStyleRunInfo
[run
].positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
453 if ((fStyleRunInfo
[run
].glyphs
== NULL
) ||
454 (fStyleRunInfo
[run
].positions
== NULL
)) {
455 status
= LE_MEMORY_ALLOCATION_ERROR
;
459 engine
->getGlyphs(fStyleRunInfo
[run
].glyphs
, layoutStatus
);
460 if (LE_FAILURE(layoutStatus
)) {
461 status
= layoutStatus
;
465 engine
->getGlyphPositions(fStyleRunInfo
[run
].positions
, layoutStatus
);
466 if (LE_FAILURE(layoutStatus
)) {
467 status
= layoutStatus
;
471 engine
->getCharIndices(&fGlyphToCharMap
[glyphBase
], runStart
, layoutStatus
);
472 if (LE_FAILURE(layoutStatus
)) {
473 status
= layoutStatus
;
477 for (glyph
= 0; glyph
< glyphCount
; glyph
+= 1) {
478 fGlyphWidths
[glyphBase
+ glyph
] = fStyleRunInfo
[run
].positions
[glyph
* 2 + 2] - fStyleRunInfo
[run
].positions
[glyph
* 2];
481 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
482 LXUtilities::reverse(&fGlyphWidths
[glyphBase
], glyphCount
);
483 LXUtilities::reverse(&fGlyphToCharMap
[glyphBase
], glyphCount
);
486 runStart
= fStyleRunLimits
[run
];
489 fStyleRunInfo
[run
].engine
= NULL
;
492 fGlyphToCharMap
[fGlyphCount
] = fCharCount
;
494 // Initialize the char-to-glyph maps to -1 so that we can later figure out
495 // whether any of the entries in the map aren't filled in below.
497 for (chIndex
= 0; chIndex
<= fCharCount
; chIndex
+= 1) {
498 fCharToMinGlyphMap
[chIndex
] = -1;
499 fCharToMaxGlyphMap
[chIndex
] = -1;
502 for (glyph
= fGlyphCount
- 1; glyph
>= 0; glyph
-= 1) {
503 le_int32 ch
= fGlyphToCharMap
[glyph
];
505 fCharToMinGlyphMap
[ch
] = glyph
;
508 fCharToMinGlyphMap
[fCharCount
] = fGlyphCount
;
510 for (glyph
= 0; glyph
< fGlyphCount
; glyph
+= 1) {
511 le_int32 ch
= fGlyphToCharMap
[glyph
];
513 fCharToMaxGlyphMap
[ch
] = glyph
;
516 fCharToMaxGlyphMap
[fCharCount
] = fGlyphCount
;
518 // Now fill in the missing values in the char-to-glyph maps.
519 fillMissingCharToGlyphMapValues(fCharToMinGlyphMap
, fCharCount
);
520 fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap
, fCharCount
);
523 ParagraphLayout::~ParagraphLayout()
525 delete (FontRuns
*) fFontRuns
;
527 if (! fClientLevels
) {
528 delete (ValueRuns
*) fLevelRuns
;
531 fClientLevels
= TRUE
;
534 if (! fClientScripts
) {
535 delete (ValueRuns
*) fScriptRuns
;
538 fClientScripts
= TRUE
;
541 if (! fClientLocales
) {
542 delete (LocaleRuns
*) fLocaleRuns
;
545 fClientLocales
= TRUE
;
548 if (fEmbeddingLevels
!= NULL
) {
549 LE_DELETE_ARRAY(fEmbeddingLevels
);
550 fEmbeddingLevels
= NULL
;
553 if (fGlyphToCharMap
!= NULL
) {
554 LE_DELETE_ARRAY(fGlyphToCharMap
);
555 fGlyphToCharMap
= NULL
;
558 if (fCharToMinGlyphMap
!= NULL
) {
559 LE_DELETE_ARRAY(fCharToMinGlyphMap
);
560 fCharToMinGlyphMap
= NULL
;
563 if (fCharToMaxGlyphMap
!= NULL
) {
564 LE_DELETE_ARRAY(fCharToMaxGlyphMap
);
565 fCharToMaxGlyphMap
= NULL
;
568 if (fGlyphWidths
!= NULL
) {
569 LE_DELETE_ARRAY(fGlyphWidths
);
573 if (fParaBidi
!= NULL
) {
574 ubidi_close(fParaBidi
);
578 if (fLineBidi
!= NULL
) {
579 ubidi_close(fLineBidi
);
583 if (fStyleRunCount
> 0) {
586 LE_DELETE_ARRAY(fStyleRunLimits
);
587 LE_DELETE_ARRAY(fStyleIndices
);
589 for (run
= 0; run
< fStyleRunCount
; run
+= 1) {
590 LE_DELETE_ARRAY(fStyleRunInfo
[run
].glyphs
);
591 LE_DELETE_ARRAY(fStyleRunInfo
[run
].positions
);
593 fStyleRunInfo
[run
].glyphs
= NULL
;
594 fStyleRunInfo
[run
].positions
= NULL
;
597 LE_DELETE_ARRAY(fStyleRunInfo
);
599 fStyleRunLimits
= NULL
;
600 fStyleIndices
= NULL
;
601 fStyleRunInfo
= NULL
;
605 if (fBreakIterator
!= NULL
) {
606 delete fBreakIterator
;
607 fBreakIterator
= NULL
;
612 le_bool
ParagraphLayout::isComplex(const LEUnicode chars
[], le_int32 count
)
614 UErrorCode scriptStatus
= U_ZERO_ERROR
;
615 UScriptCode scriptCode
= USCRIPT_INVALID_CODE
;
616 UScriptRun
*sr
= uscript_openRun(chars
, count
, &scriptStatus
);
617 le_bool result
= FALSE
;
619 while (uscript_nextRun(sr
, NULL
, NULL
, &scriptCode
)) {
620 if (isComplex(scriptCode
)) {
626 uscript_closeRun(sr
);
630 le_int32
ParagraphLayout::getAscent() const
632 if (fAscent
<= 0 && fCharCount
> 0) {
633 ((ParagraphLayout
*) this)->computeMetrics();
639 le_int32
ParagraphLayout::getDescent() const
641 if (fAscent
<= 0 && fCharCount
> 0) {
642 ((ParagraphLayout
*) this)->computeMetrics();
648 le_int32
ParagraphLayout::getLeading() const
650 if (fAscent
<= 0 && fCharCount
> 0) {
651 ((ParagraphLayout
*) this)->computeMetrics();
657 le_bool
ParagraphLayout::isDone() const
659 return fLineEnd
>= fCharCount
;
662 ParagraphLayout::Line
*ParagraphLayout::nextLine(float width
)
668 fLineStart
= fLineEnd
;
671 le_int32 glyph
= fCharToMinGlyphMap
[fLineStart
];
672 float widthSoFar
= 0;
674 while (glyph
< fGlyphCount
&& widthSoFar
+ fGlyphWidths
[glyph
] <= width
) {
675 widthSoFar
+= fGlyphWidths
[glyph
++];
678 // If no glyphs fit on the line, force one to fit.
680 // (There shouldn't be any zero width glyphs at the
681 // start of a line unless the paragraph consists of
682 // only zero width glyphs, because otherwise the zero
683 // width glyphs will have been included on the end of
684 // the previous line...)
685 if (widthSoFar
== 0 && glyph
< fGlyphCount
) {
689 fLineEnd
= previousBreak(fGlyphToCharMap
[glyph
]);
691 // If this break is at or before the last one,
692 // find a glyph, starting at the one which didn't
693 // fit, that produces a break after the last one.
694 while (fLineEnd
<= fLineStart
) {
695 fLineEnd
= fGlyphToCharMap
[glyph
++];
698 fLineEnd
= fCharCount
;
701 return computeVisualRuns();
704 void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel
)
706 UErrorCode bidiStatus
= U_ZERO_ERROR
;
708 if (fLevelRuns
!= NULL
) {
712 fEmbeddingLevels
= LE_NEW_ARRAY(UBiDiLevel
, fCharCount
);
714 for (ch
= 0, run
= 0; run
< fLevelRuns
->getCount(); run
+= 1) {
715 UBiDiLevel runLevel
= (UBiDiLevel
) fLevelRuns
->getValue(run
) | UBIDI_LEVEL_OVERRIDE
;
716 le_int32 runLimit
= fLevelRuns
->getLimit(run
);
718 while (ch
< runLimit
) {
719 fEmbeddingLevels
[ch
++] = runLevel
;
724 fParaBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
725 ubidi_setPara(fParaBidi
, fChars
, fCharCount
, paragraphLevel
, fEmbeddingLevels
, &bidiStatus
);
727 if (fLevelRuns
== NULL
) {
728 le_int32 levelRunCount
= ubidi_countRuns(fParaBidi
, &bidiStatus
);
729 ValueRuns
*levelRuns
= new ValueRuns(levelRunCount
);
731 le_int32 logicalStart
= 0;
736 for (run
= 0; run
< levelRunCount
; run
+= 1) {
737 ubidi_getLogicalRun(fParaBidi
, logicalStart
, &limit
, &level
);
738 levelRuns
->add(level
, limit
);
739 logicalStart
= limit
;
742 fLevelRuns
= levelRuns
;
743 fClientLevels
= FALSE
;
747 void ParagraphLayout::computeScripts()
749 UErrorCode scriptStatus
= U_ZERO_ERROR
;
750 UScriptRun
*sr
= uscript_openRun(fChars
, fCharCount
, &scriptStatus
);
751 ValueRuns
*scriptRuns
= new ValueRuns(0);
755 while (uscript_nextRun(sr
, NULL
, &limit
, &script
)) {
756 scriptRuns
->add(script
, limit
);
759 uscript_closeRun(sr
);
761 fScriptRuns
= scriptRuns
;
762 fClientScripts
= FALSE
;
765 void ParagraphLayout::computeLocales()
767 LocaleRuns
*localeRuns
= new LocaleRuns(0);
768 const Locale
*defaultLocale
= &Locale::getDefault();
770 localeRuns
->add(defaultLocale
, fCharCount
);
772 fLocaleRuns
= localeRuns
;
773 fClientLocales
= FALSE
;
776 void ParagraphLayout::computeSubFonts(const FontRuns
*fontRuns
, LEErrorCode
&status
)
778 if (LE_FAILURE(status
)) {
782 const RunArray
*styleRunArrays
[] = {fontRuns
, fScriptRuns
};
783 le_int32 styleCount
= sizeof styleRunArrays
/ sizeof styleRunArrays
[0];
784 StyleRuns
styleRuns(styleRunArrays
, styleCount
);
785 le_int32 styleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
786 le_int32
*styleRunLimits
= LE_NEW_ARRAY(le_int32
, styleRunCount
);
787 le_int32
*styleIndices
= LE_NEW_ARRAY(le_int32
, styleRunCount
* styleCount
);
788 FontRuns
*subFontRuns
= new FontRuns(0);
789 le_int32 run
, offset
, *si
;
791 styleRuns
.getRuns(styleRunLimits
, styleIndices
);
796 for (run
= 0; run
< styleRunCount
; run
+= 1) {
797 const LEFontInstance
*runFont
= fontRuns
->getFont(si
[0]);
798 le_int32 script
= fScriptRuns
->getValue(si
[1]);
800 while (offset
< styleRunLimits
[run
]) {
801 const LEFontInstance
*subFont
= runFont
->getSubFont(fChars
, &offset
, styleRunLimits
[run
], script
, status
);
803 if (LE_FAILURE(status
)) {
808 subFontRuns
->add(subFont
, offset
);
814 fFontRuns
= subFontRuns
;
817 LE_DELETE_ARRAY(styleIndices
);
818 LE_DELETE_ARRAY(styleRunLimits
);
821 void ParagraphLayout::computeMetrics()
823 le_int32 i
, count
= fFontRuns
->getCount();
826 for (i
= 0; i
< count
; i
+= 1) {
827 const LEFontInstance
*font
= fFontRuns
->getFont(i
);
828 le_int32 ascent
= font
->getAscent();
829 le_int32 descent
= font
->getDescent();
830 le_int32 leading
= font
->getLeading();
831 le_int32 dl
= descent
+ leading
;
833 if (ascent
> fAscent
) {
837 if (descent
> fDescent
) {
841 if (leading
> fLeading
) {
850 fLeading
= maxDL
- fDescent
;
856 const char *localeCode
;
857 le_int32 languageCode
;
860 static const LanguageMap languageMap
[] =
862 {"afr", afkLanguageCode
}, // Afrikaans
863 {"ara", araLanguageCode
}, // Arabic
864 {"asm", asmLanguageCode
}, // Assamese
865 {"bel", belLanguageCode
}, // Belarussian
866 {"ben", benLanguageCode
}, // Bengali
867 {"bod", tibLanguageCode
}, // Tibetan
868 {"bul", bgrLanguageCode
}, // Bulgarian
869 {"cat", catLanguageCode
}, // Catalan
870 {"ces", csyLanguageCode
}, // Czech
871 {"che", cheLanguageCode
}, // Chechen
872 {"cop", copLanguageCode
}, // Coptic
873 {"cym", welLanguageCode
}, // Welsh
874 {"dan", danLanguageCode
}, // Danish
875 {"deu", deuLanguageCode
}, // German
876 {"dzo", dznLanguageCode
}, // Dzongkha
877 {"ell", ellLanguageCode
}, // Greek
878 {"eng", engLanguageCode
}, // English
879 {"est", etiLanguageCode
}, // Estonian
880 {"eus", euqLanguageCode
}, // Basque
881 {"fas", farLanguageCode
}, // Farsi
882 {"fin", finLanguageCode
}, // Finnish
883 {"fra", fraLanguageCode
}, // French
884 {"gle", gaeLanguageCode
}, // Irish Gaelic
885 {"guj", gujLanguageCode
}, // Gujarati
886 {"hau", hauLanguageCode
}, // Hausa
887 {"heb", iwrLanguageCode
}, // Hebrew
888 {"hin", hinLanguageCode
}, // Hindi
889 {"hrv", hrvLanguageCode
}, // Croatian
890 {"hun", hunLanguageCode
}, // Hungarian
891 {"hye", hyeLanguageCode
}, // Armenian
892 {"ind", indLanguageCode
}, // Indonesian
893 {"ita", itaLanguageCode
}, // Italian
894 {"jpn", janLanguageCode
}, // Japanese
895 {"kan", kanLanguageCode
}, // Kannada
896 {"kas", kshLanguageCode
}, // Kashmiri
897 {"khm", khmLanguageCode
}, // Khmer
898 {"kok", kokLanguageCode
}, // Konkani
899 {"kor", korLanguageCode
}, // Korean
900 // {"mal_XXX", malLanguageCode}, // Malayalam - Traditional
901 {"mal", mlrLanguageCode
}, // Malayalam - Reformed
902 {"mar", marLanguageCode
}, // Marathi
903 {"mlt", mtsLanguageCode
}, // Maltese
904 {"mni", mniLanguageCode
}, // Manipuri
905 {"mon", mngLanguageCode
}, // Mongolian
906 {"nep", nepLanguageCode
}, // Nepali
907 {"ori", oriLanguageCode
}, // Oriya
908 {"pol", plkLanguageCode
}, // Polish
909 {"por", ptgLanguageCode
}, // Portuguese
910 {"pus", pasLanguageCode
}, // Pashto
911 {"ron", romLanguageCode
}, // Romanian
912 {"rus", rusLanguageCode
}, // Russian
913 {"san", sanLanguageCode
}, // Sanskrit
914 {"sin", snhLanguageCode
}, // Sinhalese
915 {"slk", skyLanguageCode
}, // Slovak
916 {"snd", sndLanguageCode
}, // Sindhi
917 {"slv", slvLanguageCode
}, // Slovenian
918 {"spa", espLanguageCode
}, // Spanish
919 {"sqi", sqiLanguageCode
}, // Albanian
920 {"srp", srbLanguageCode
}, // Serbian
921 {"swe", sveLanguageCode
}, // Swedish
922 {"syr", syrLanguageCode
}, // Syriac
923 {"tam", tamLanguageCode
}, // Tamil
924 {"tel", telLanguageCode
}, // Telugu
925 {"tha", thaLanguageCode
}, // Thai
926 {"tur", trkLanguageCode
}, // Turkish
927 {"urd", urdLanguageCode
}, // Urdu
928 {"yid", jiiLanguageCode
}, // Yiddish
929 // {"zhp", zhpLanguageCode}, // Chinese - Phonetic
930 {"zho", zhsLanguageCode
}, // Chinese
931 {"zho_CHN", zhsLanguageCode
}, // Chinese - China
932 {"zho_HKG", zhsLanguageCode
}, // Chinese - Hong Kong
933 {"zho_MAC", zhtLanguageCode
}, // Chinese - Macao
934 {"zho_SGP", zhsLanguageCode
}, // Chinese - Singapore
935 {"zho_TWN", zhtLanguageCode
} // Chinese - Taiwan
938 static const le_int32 languageMapCount
= ARRAY_SIZE(languageMap
);
940 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
942 char code
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
943 const char *language
= locale
->getISO3Language();
944 const char *country
= locale
->getISO3Country();
946 uprv_strcat(code
, language
);
948 if ((uprv_strcmp(language
, "zho") == 0) && country
!= NULL
) {
949 uprv_strcat(code
, "_");
950 uprv_strcat(code
, country
);
953 for (le_int32 i
= 0; i
< languageMapCount
; i
+= 1) {
954 if (uprv_strcmp(code
, languageMap
[i
].localeCode
) == 0) {
955 return languageMap
[i
].languageCode
;
959 return nullLanguageCode
;
963 // TODO - dummy implementation for right now...
964 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
966 return nullLanguageCode
;
970 le_bool
ParagraphLayout::isComplex(UScriptCode script
)
972 if (script
< 0 || script
>= (UScriptCode
) scriptCodeCount
) {
976 return complexTable
[script
];
979 le_int32
ParagraphLayout::previousBreak(le_int32 charIndex
)
981 // skip over any whitespace or control characters,
982 // because they can hang in the margin.
983 while (charIndex
< fCharCount
&&
984 (u_isWhitespace(fChars
[charIndex
]) ||
985 u_iscntrl(fChars
[charIndex
]))) {
989 // Create the BreakIterator if we don't already have one
990 if (fBreakIterator
== NULL
) {
992 UCharCharacterIterator
*iter
= new UCharCharacterIterator(fChars
, fCharCount
);
993 UErrorCode status
= U_ZERO_ERROR
;
995 fBreakIterator
= BreakIterator::createLineInstance(thai
, status
);
996 fBreakIterator
->adoptText(iter
);
999 // return the break location that's at or before
1000 // the character we stopped on. Note: if we're
1001 // on a break, the "+ 1" will cause preceding to
1003 return fBreakIterator
->preceding(charIndex
+ 1);
1006 ParagraphLayout::Line
*ParagraphLayout::computeVisualRuns()
1008 UErrorCode bidiStatus
= U_ZERO_ERROR
;
1009 le_int32 dirRunCount
, visualRun
;
1011 fVisualRunLastX
= 0;
1012 fVisualRunLastY
= 0;
1013 fFirstVisualRun
= getCharRun(fLineStart
);
1014 fLastVisualRun
= getCharRun(fLineEnd
- 1);
1016 if (fLineBidi
== NULL
) {
1017 fLineBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
1020 ubidi_setLine(fParaBidi
, fLineStart
, fLineEnd
, fLineBidi
, &bidiStatus
);
1021 dirRunCount
= ubidi_countRuns(fLineBidi
, &bidiStatus
);
1023 Line
*line
= new Line();
1025 for (visualRun
= 0; visualRun
< dirRunCount
; visualRun
+= 1) {
1026 le_int32 relStart
, run
, runLength
;
1027 UBiDiDirection runDirection
= ubidi_getVisualRun(fLineBidi
, visualRun
, &relStart
, &runLength
);
1028 le_int32 runStart
= fLineStart
+ relStart
;
1029 le_int32 runEnd
= runStart
+ runLength
- 1;
1030 le_int32 firstRun
= getCharRun(runStart
);
1031 le_int32 lastRun
= getCharRun(runEnd
);
1032 le_int32 startRun
= (runDirection
== UBIDI_LTR
)? firstRun
: lastRun
;
1033 le_int32 stopRun
= (runDirection
== UBIDI_LTR
)? lastRun
+ 1 : firstRun
- 1;
1034 le_int32 dir
= (runDirection
== UBIDI_LTR
)? 1 : -1;
1036 for (run
= startRun
; run
!= stopRun
; run
+= dir
) {
1037 le_int32 firstChar
= (run
== firstRun
)? runStart
: fStyleRunInfo
[run
].runBase
;
1038 le_int32 lastChar
= (run
== lastRun
)? runEnd
: fStyleRunInfo
[run
].runLimit
- 1;
1040 appendRun(line
, run
, firstChar
, lastChar
);
1047 void ParagraphLayout::appendRun(ParagraphLayout::Line
*line
, le_int32 run
, le_int32 firstChar
, le_int32 lastChar
)
1049 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
1050 le_int32 inGlyph
, outGlyph
;
1052 // Get the glyph indices for all the characters between firstChar and lastChar,
1053 // make the minimum one be leftGlyph and the maximum one be rightGlyph.
1054 // (need to do this to handle local reorderings like Indic left matras)
1055 le_int32 leftGlyph
= fGlyphCount
;
1056 le_int32 rightGlyph
= -1;
1059 for (ch
= firstChar
; ch
<= lastChar
; ch
+= 1) {
1060 le_int32 minGlyph
= fCharToMinGlyphMap
[ch
];
1061 le_int32 maxGlyph
= fCharToMaxGlyphMap
[ch
];
1063 if (minGlyph
< leftGlyph
) {
1064 leftGlyph
= minGlyph
;
1067 if (maxGlyph
> rightGlyph
) {
1068 rightGlyph
= maxGlyph
;
1072 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
1073 le_int32 swap
= rightGlyph
;
1074 le_int32 last
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
1076 // Here, we want to remove the glyphBase bias...
1077 rightGlyph
= last
- leftGlyph
;
1078 leftGlyph
= last
- swap
;
1080 rightGlyph
-= glyphBase
;
1081 leftGlyph
-= glyphBase
;
1084 // Set the position bias for the glyphs. If we're at the start of
1085 // a line, we want the first glyph to be at x = 0, even if it comes
1086 // from the middle of a layout. If we've got a right-to-left run, we
1087 // want the left-most glyph to start at the final x position of the
1088 // previous run, even though this glyph may be in the middle of the
1090 fVisualRunLastX
-= fStyleRunInfo
[run
].positions
[leftGlyph
* 2];
1092 // Make rightGlyph be the glyph just to the right of
1096 UBiDiDirection direction
= ((fStyleRunInfo
[run
].level
& 1) == 0)? UBIDI_LTR
: UBIDI_RTL
;
1097 le_int32 glyphCount
= rightGlyph
- leftGlyph
;
1098 LEGlyphID
*glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
1099 float *positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
1100 le_int32
*glyphToCharMap
= LE_NEW_ARRAY(le_int32
, glyphCount
);
1102 LE_ARRAY_COPY(glyphs
, &fStyleRunInfo
[run
].glyphs
[leftGlyph
], glyphCount
);
1104 for (outGlyph
= 0, inGlyph
= leftGlyph
* 2; inGlyph
<= rightGlyph
* 2; inGlyph
+= 2, outGlyph
+= 2) {
1105 positions
[outGlyph
] = fStyleRunInfo
[run
].positions
[inGlyph
] + fVisualRunLastX
;
1106 positions
[outGlyph
+ 1] = fStyleRunInfo
[run
].positions
[inGlyph
+ 1] + fVisualRunLastY
;
1109 // Save the ending position of this run
1110 // to use for the start of the next run
1111 fVisualRunLastX
= positions
[outGlyph
- 2];
1112 fVisualRunLastY
= positions
[outGlyph
- 1];
1114 if ((fStyleRunInfo
[run
].level
& 1) == 0) {
1115 for (outGlyph
= 0, inGlyph
= leftGlyph
; inGlyph
< rightGlyph
; inGlyph
+= 1, outGlyph
+= 1) {
1116 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[glyphBase
+ inGlyph
];
1119 // Because fGlyphToCharMap is stored in logical order to facilitate line breaking,
1120 // we need to map the physical glyph indices to logical indices while we copy the
1121 // character indices.
1122 le_int32 base
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
1124 for (outGlyph
= 0, inGlyph
= leftGlyph
; inGlyph
< rightGlyph
; inGlyph
+= 1, outGlyph
+= 1) {
1125 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[base
- inGlyph
];
1129 line
->append(fStyleRunInfo
[run
].font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1132 le_int32
ParagraphLayout::getCharRun(le_int32 charIndex
)
1134 if (charIndex
< 0 || charIndex
> fCharCount
) {
1140 // NOTE: as long as fStyleRunLimits is well-formed
1141 // the above range check guarantees that we'll never
1142 // fall off the end of the array.
1144 while (charIndex
>= fStyleRunLimits
[run
]) {
1152 const char ParagraphLayout::Line::fgClassID
= 0;
1154 #define INITIAL_RUN_CAPACITY 4
1155 #define RUN_CAPACITY_GROW_LIMIT 16
1157 ParagraphLayout::Line::~Line()
1161 for (i
= 0; i
< fRunCount
; i
+= 1) {
1165 LE_DELETE_ARRAY(fRuns
);
1168 le_int32
ParagraphLayout::Line::getAscent() const
1171 ((ParagraphLayout::Line
*)this)->computeMetrics();
1177 le_int32
ParagraphLayout::Line::getDescent() const
1180 ((ParagraphLayout::Line
*)this)->computeMetrics();
1186 le_int32
ParagraphLayout::Line::getLeading() const
1189 ((ParagraphLayout::Line
*)this)->computeMetrics();
1195 le_int32
ParagraphLayout::Line::getWidth() const
1197 const VisualRun
*lastRun
= getVisualRun(fRunCount
- 1);
1199 if (lastRun
== NULL
) {
1203 le_int32 glyphCount
= lastRun
->getGlyphCount();
1204 const float *positions
= lastRun
->getPositions();
1206 return (le_int32
) positions
[glyphCount
* 2];
1209 const ParagraphLayout::VisualRun
*ParagraphLayout::Line::getVisualRun(le_int32 runIndex
) const
1211 if (runIndex
< 0 || runIndex
>= fRunCount
) {
1215 return fRuns
[runIndex
];
1218 void ParagraphLayout::Line::append(const LEFontInstance
*font
, UBiDiDirection direction
, le_int32 glyphCount
,
1219 const LEGlyphID glyphs
[], const float positions
[], const le_int32 glyphToCharMap
[])
1221 if (fRunCount
>= fRunCapacity
) {
1222 if (fRunCapacity
== 0) {
1223 fRunCapacity
= INITIAL_RUN_CAPACITY
;
1224 fRuns
= LE_NEW_ARRAY(ParagraphLayout::VisualRun
*, fRunCapacity
);
1226 fRunCapacity
+= (fRunCapacity
< RUN_CAPACITY_GROW_LIMIT
? fRunCapacity
: RUN_CAPACITY_GROW_LIMIT
);
1227 fRuns
= (ParagraphLayout::VisualRun
**) LE_GROW_ARRAY(fRuns
, fRunCapacity
);
1231 fRuns
[fRunCount
++] = new ParagraphLayout::VisualRun(font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1234 void ParagraphLayout::Line::computeMetrics()
1238 for (le_int32 i
= 0; i
< fRunCount
; i
+= 1) {
1239 le_int32 ascent
= fRuns
[i
]->getAscent();
1240 le_int32 descent
= fRuns
[i
]->getDescent();
1241 le_int32 leading
= fRuns
[i
]->getLeading();
1242 le_int32 dl
= descent
+ leading
;
1244 if (ascent
> fAscent
) {
1248 if (descent
> fDescent
) {
1252 if (leading
> fLeading
) {
1261 fLeading
= maxDL
- fDescent
;
1264 const char ParagraphLayout::VisualRun::fgClassID
= 0;
1266 ParagraphLayout::VisualRun::~VisualRun()
1268 LE_DELETE_ARRAY(fGlyphToCharMap
);
1269 LE_DELETE_ARRAY(fPositions
);
1270 LE_DELETE_ARRAY(fGlyphs
);