2 **********************************************************************
3 * Copyright (C) 2002-2014, 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 (void)copyright
; // Suppress unused variable warning.
336 (void)fVertical
; // Suppress warning for unused field fVertical.
338 // FIXME: should check the limit arrays for consistency...
340 computeLevels(paragraphLevel
);
342 if (scriptRuns
== NULL
) {
346 if (localeRuns
== NULL
) {
350 computeSubFonts(fontRuns
, status
);
352 if (LE_FAILURE(status
)) {
358 // now intersect the font, direction and script runs...
359 const RunArray
*styleRunArrays
[] = {fFontRuns
, fLevelRuns
, fScriptRuns
, fLocaleRuns
};
360 le_int32 styleCount
= sizeof styleRunArrays
/ sizeof styleRunArrays
[0];
361 StyleRuns
styleRuns(styleRunArrays
, styleCount
);
362 LEErrorCode layoutStatus
= LE_NO_ERROR
;
364 fStyleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
366 fStyleRunLimits
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
);
367 fStyleIndices
= LE_NEW_ARRAY(le_int32
, fStyleRunCount
* styleCount
);
368 if ((fStyleRunLimits
== NULL
) || (fStyleIndices
== NULL
)) {
369 status
= LE_MEMORY_ALLOCATION_ERROR
;
373 styleRuns
.getRuns(fStyleRunLimits
, fStyleIndices
);
375 // now build a LayoutEngine for each style run...
376 le_int32
*styleIndices
= fStyleIndices
;
377 le_int32 run
, runStart
;
379 fStyleRunInfo
= LE_NEW_ARRAY(StyleRunInfo
, fStyleRunCount
);
380 if (fStyleRunInfo
== NULL
) {
381 status
= LE_MEMORY_ALLOCATION_ERROR
;
386 for (run
= 0; run
< fStyleRunCount
; run
+= 1) {
387 fStyleRunInfo
[run
].font
= NULL
;
388 fStyleRunInfo
[run
].runBase
= 0;
389 fStyleRunInfo
[run
].runLimit
= 0;
390 fStyleRunInfo
[run
].script
= (UScriptCode
)0;
391 fStyleRunInfo
[run
].locale
= NULL
;
392 fStyleRunInfo
[run
].level
= 0;
393 fStyleRunInfo
[run
].glyphBase
= 0;
394 fStyleRunInfo
[run
].engine
= NULL
;
395 fStyleRunInfo
[run
].glyphCount
= 0;
396 fStyleRunInfo
[run
].glyphs
= NULL
;
397 fStyleRunInfo
[run
].positions
= NULL
;
402 for (runStart
= 0, run
= 0; run
< fStyleRunCount
; run
+= 1) {
403 fStyleRunInfo
[run
].font
= fFontRuns
->getFont(styleIndices
[0]);
404 fStyleRunInfo
[run
].runBase
= runStart
;
405 fStyleRunInfo
[run
].runLimit
= fStyleRunLimits
[run
];
406 fStyleRunInfo
[run
].script
= (UScriptCode
) fScriptRuns
->getValue(styleIndices
[2]);
407 fStyleRunInfo
[run
].locale
= fLocaleRuns
->getLocale(styleIndices
[3]);
408 fStyleRunInfo
[run
].level
= (UBiDiLevel
) fLevelRuns
->getValue(styleIndices
[1]);
409 fStyleRunInfo
[run
].glyphBase
= fGlyphCount
;
411 fStyleRunInfo
[run
].engine
= LayoutEngine::layoutEngineFactory(fStyleRunInfo
[run
].font
,
412 fStyleRunInfo
[run
].script
, getLanguageCode(fStyleRunInfo
[run
].locale
), layoutStatus
);
413 if (LE_FAILURE(layoutStatus
)) {
414 status
= layoutStatus
;
418 fStyleRunInfo
[run
].glyphCount
= fStyleRunInfo
[run
].engine
->layoutChars(fChars
, runStart
, fStyleRunLimits
[run
] - runStart
, fCharCount
,
419 fStyleRunInfo
[run
].level
& 1, 0, 0, layoutStatus
);
420 if (LE_FAILURE(layoutStatus
)) {
421 status
= layoutStatus
;
425 runStart
= fStyleRunLimits
[run
];
426 styleIndices
+= styleCount
;
427 fGlyphCount
+= fStyleRunInfo
[run
].glyphCount
;
430 // Make big arrays for the glyph widths, glyph-to-char and char-to-glyph maps,
431 // in logical order. (Both maps need an extra entry for the end of the text.)
433 // For each layout get the positions and convert them into glyph widths, in
434 // logical order. Get the glyph-to-char mapping, offset by starting index in the
435 // character array. Swap the glyph width and glyph-to-char arrays into logical order.
436 // Finally, fill in the char-to-glyph mappings.
437 fGlyphWidths
= LE_NEW_ARRAY(float, fGlyphCount
);
438 fGlyphToCharMap
= LE_NEW_ARRAY(le_int32
, fGlyphCount
+ 1);
439 fCharToMinGlyphMap
= LE_NEW_ARRAY(le_int32
, fCharCount
+ 1);
440 fCharToMaxGlyphMap
= LE_NEW_ARRAY(le_int32
, fCharCount
+ 1);
441 if ((fGlyphWidths
== NULL
) || (fGlyphToCharMap
== NULL
) ||
442 (fCharToMinGlyphMap
== NULL
) || (fCharToMaxGlyphMap
== NULL
)) {
443 status
= LE_MEMORY_ALLOCATION_ERROR
;
449 for (runStart
= 0, run
= 0; run
< fStyleRunCount
; run
+= 1) {
450 LayoutEngine
*engine
= fStyleRunInfo
[run
].engine
;
451 le_int32 glyphCount
= fStyleRunInfo
[run
].glyphCount
;
452 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
454 fStyleRunInfo
[run
].glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
455 fStyleRunInfo
[run
].positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
456 if ((fStyleRunInfo
[run
].glyphs
== NULL
) ||
457 (fStyleRunInfo
[run
].positions
== NULL
)) {
458 status
= LE_MEMORY_ALLOCATION_ERROR
;
462 engine
->getGlyphs(fStyleRunInfo
[run
].glyphs
, layoutStatus
);
463 if (LE_FAILURE(layoutStatus
)) {
464 status
= layoutStatus
;
468 engine
->getGlyphPositions(fStyleRunInfo
[run
].positions
, layoutStatus
);
469 if (LE_FAILURE(layoutStatus
)) {
470 status
= layoutStatus
;
474 engine
->getCharIndices(&fGlyphToCharMap
[glyphBase
], runStart
, layoutStatus
);
475 if (LE_FAILURE(layoutStatus
)) {
476 status
= layoutStatus
;
480 for (glyph
= 0; glyph
< glyphCount
; glyph
+= 1) {
481 fGlyphWidths
[glyphBase
+ glyph
] = fStyleRunInfo
[run
].positions
[glyph
* 2 + 2] - fStyleRunInfo
[run
].positions
[glyph
* 2];
484 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
485 LXUtilities::reverse(&fGlyphWidths
[glyphBase
], glyphCount
);
486 LXUtilities::reverse(&fGlyphToCharMap
[glyphBase
], glyphCount
);
489 runStart
= fStyleRunLimits
[run
];
492 fStyleRunInfo
[run
].engine
= NULL
;
495 fGlyphToCharMap
[fGlyphCount
] = fCharCount
;
497 // Initialize the char-to-glyph maps to -1 so that we can later figure out
498 // whether any of the entries in the map aren't filled in below.
500 for (chIndex
= 0; chIndex
<= fCharCount
; chIndex
+= 1) {
501 fCharToMinGlyphMap
[chIndex
] = -1;
502 fCharToMaxGlyphMap
[chIndex
] = -1;
505 for (glyph
= fGlyphCount
- 1; glyph
>= 0; glyph
-= 1) {
506 le_int32 ch
= fGlyphToCharMap
[glyph
];
508 fCharToMinGlyphMap
[ch
] = glyph
;
511 fCharToMinGlyphMap
[fCharCount
] = fGlyphCount
;
513 for (glyph
= 0; glyph
< fGlyphCount
; glyph
+= 1) {
514 le_int32 ch
= fGlyphToCharMap
[glyph
];
516 fCharToMaxGlyphMap
[ch
] = glyph
;
519 fCharToMaxGlyphMap
[fCharCount
] = fGlyphCount
;
521 // Now fill in the missing values in the char-to-glyph maps.
522 fillMissingCharToGlyphMapValues(fCharToMinGlyphMap
, fCharCount
);
523 fillMissingCharToGlyphMapValues(fCharToMaxGlyphMap
, fCharCount
);
526 ParagraphLayout::~ParagraphLayout()
528 delete (FontRuns
*) fFontRuns
;
530 if (! fClientLevels
) {
531 delete (ValueRuns
*) fLevelRuns
;
534 fClientLevels
= TRUE
;
537 if (! fClientScripts
) {
538 delete (ValueRuns
*) fScriptRuns
;
541 fClientScripts
= TRUE
;
544 if (! fClientLocales
) {
545 delete (LocaleRuns
*) fLocaleRuns
;
548 fClientLocales
= TRUE
;
551 if (fEmbeddingLevels
!= NULL
) {
552 LE_DELETE_ARRAY(fEmbeddingLevels
);
553 fEmbeddingLevels
= NULL
;
556 if (fGlyphToCharMap
!= NULL
) {
557 LE_DELETE_ARRAY(fGlyphToCharMap
);
558 fGlyphToCharMap
= NULL
;
561 if (fCharToMinGlyphMap
!= NULL
) {
562 LE_DELETE_ARRAY(fCharToMinGlyphMap
);
563 fCharToMinGlyphMap
= NULL
;
566 if (fCharToMaxGlyphMap
!= NULL
) {
567 LE_DELETE_ARRAY(fCharToMaxGlyphMap
);
568 fCharToMaxGlyphMap
= NULL
;
571 if (fGlyphWidths
!= NULL
) {
572 LE_DELETE_ARRAY(fGlyphWidths
);
576 if (fParaBidi
!= NULL
) {
577 ubidi_close(fParaBidi
);
581 if (fLineBidi
!= NULL
) {
582 ubidi_close(fLineBidi
);
586 if (fStyleRunCount
> 0) {
589 LE_DELETE_ARRAY(fStyleRunLimits
);
590 LE_DELETE_ARRAY(fStyleIndices
);
592 for (run
= 0; run
< fStyleRunCount
; run
+= 1) {
593 LE_DELETE_ARRAY(fStyleRunInfo
[run
].glyphs
);
594 LE_DELETE_ARRAY(fStyleRunInfo
[run
].positions
);
596 fStyleRunInfo
[run
].glyphs
= NULL
;
597 fStyleRunInfo
[run
].positions
= NULL
;
600 LE_DELETE_ARRAY(fStyleRunInfo
);
602 fStyleRunLimits
= NULL
;
603 fStyleIndices
= NULL
;
604 fStyleRunInfo
= NULL
;
608 if (fBreakIterator
!= NULL
) {
609 delete fBreakIterator
;
610 fBreakIterator
= NULL
;
615 le_bool
ParagraphLayout::isComplex(const LEUnicode chars
[], le_int32 count
)
617 UErrorCode scriptStatus
= U_ZERO_ERROR
;
618 UScriptCode scriptCode
= USCRIPT_INVALID_CODE
;
619 UScriptRun
*sr
= uscript_openRun(chars
, count
, &scriptStatus
);
620 le_bool result
= FALSE
;
622 while (uscript_nextRun(sr
, NULL
, NULL
, &scriptCode
)) {
623 if (isComplex(scriptCode
)) {
629 uscript_closeRun(sr
);
633 le_int32
ParagraphLayout::getAscent() const
635 if (fAscent
<= 0 && fCharCount
> 0) {
636 ((ParagraphLayout
*) this)->computeMetrics();
642 le_int32
ParagraphLayout::getDescent() const
644 if (fAscent
<= 0 && fCharCount
> 0) {
645 ((ParagraphLayout
*) this)->computeMetrics();
651 le_int32
ParagraphLayout::getLeading() const
653 if (fAscent
<= 0 && fCharCount
> 0) {
654 ((ParagraphLayout
*) this)->computeMetrics();
660 le_bool
ParagraphLayout::isDone() const
662 return fLineEnd
>= fCharCount
;
665 ParagraphLayout::Line
*ParagraphLayout::nextLine(float width
)
671 fLineStart
= fLineEnd
;
674 le_int32 glyph
= fCharToMinGlyphMap
[fLineStart
];
675 float widthSoFar
= 0;
677 while (glyph
< fGlyphCount
&& widthSoFar
+ fGlyphWidths
[glyph
] <= width
) {
678 widthSoFar
+= fGlyphWidths
[glyph
++];
681 // If no glyphs fit on the line, force one to fit.
683 // (There shouldn't be any zero width glyphs at the
684 // start of a line unless the paragraph consists of
685 // only zero width glyphs, because otherwise the zero
686 // width glyphs will have been included on the end of
687 // the previous line...)
688 if (widthSoFar
== 0 && glyph
< fGlyphCount
) {
692 fLineEnd
= previousBreak(fGlyphToCharMap
[glyph
]);
694 // If this break is at or before the last one,
695 // find a glyph, starting at the one which didn't
696 // fit, that produces a break after the last one.
697 while (fLineEnd
<= fLineStart
) {
698 fLineEnd
= fGlyphToCharMap
[glyph
++];
701 fLineEnd
= fCharCount
;
704 return computeVisualRuns();
707 void ParagraphLayout::computeLevels(UBiDiLevel paragraphLevel
)
709 UErrorCode bidiStatus
= U_ZERO_ERROR
;
711 if (fLevelRuns
!= NULL
) {
715 fEmbeddingLevels
= LE_NEW_ARRAY(UBiDiLevel
, fCharCount
);
717 for (ch
= 0, run
= 0; run
< fLevelRuns
->getCount(); run
+= 1) {
718 UBiDiLevel runLevel
= (UBiDiLevel
) fLevelRuns
->getValue(run
) | UBIDI_LEVEL_OVERRIDE
;
719 le_int32 runLimit
= fLevelRuns
->getLimit(run
);
721 while (ch
< runLimit
) {
722 fEmbeddingLevels
[ch
++] = runLevel
;
727 fParaBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
728 ubidi_setPara(fParaBidi
, fChars
, fCharCount
, paragraphLevel
, fEmbeddingLevels
, &bidiStatus
);
730 if (fLevelRuns
== NULL
) {
731 le_int32 levelRunCount
= ubidi_countRuns(fParaBidi
, &bidiStatus
);
732 ValueRuns
*levelRuns
= new ValueRuns(levelRunCount
);
734 le_int32 logicalStart
= 0;
739 for (run
= 0; run
< levelRunCount
; run
+= 1) {
740 ubidi_getLogicalRun(fParaBidi
, logicalStart
, &limit
, &level
);
741 levelRuns
->add(level
, limit
);
742 logicalStart
= limit
;
745 fLevelRuns
= levelRuns
;
746 fClientLevels
= FALSE
;
750 void ParagraphLayout::computeScripts()
752 UErrorCode scriptStatus
= U_ZERO_ERROR
;
753 UScriptRun
*sr
= uscript_openRun(fChars
, fCharCount
, &scriptStatus
);
754 ValueRuns
*scriptRuns
= new ValueRuns(0);
758 while (uscript_nextRun(sr
, NULL
, &limit
, &script
)) {
759 scriptRuns
->add(script
, limit
);
762 uscript_closeRun(sr
);
764 fScriptRuns
= scriptRuns
;
765 fClientScripts
= FALSE
;
768 void ParagraphLayout::computeLocales()
770 LocaleRuns
*localeRuns
= new LocaleRuns(0);
771 const Locale
*defaultLocale
= &Locale::getDefault();
773 localeRuns
->add(defaultLocale
, fCharCount
);
775 fLocaleRuns
= localeRuns
;
776 fClientLocales
= FALSE
;
779 void ParagraphLayout::computeSubFonts(const FontRuns
*fontRuns
, LEErrorCode
&status
)
781 if (LE_FAILURE(status
)) {
785 const RunArray
*styleRunArrays
[] = {fontRuns
, fScriptRuns
};
786 le_int32 styleCount
= sizeof styleRunArrays
/ sizeof styleRunArrays
[0];
787 StyleRuns
styleRuns(styleRunArrays
, styleCount
);
788 le_int32 styleRunCount
= styleRuns
.getRuns(NULL
, NULL
);
789 le_int32
*styleRunLimits
= LE_NEW_ARRAY(le_int32
, styleRunCount
);
790 le_int32
*styleIndices
= LE_NEW_ARRAY(le_int32
, styleRunCount
* styleCount
);
791 FontRuns
*subFontRuns
= new FontRuns(0);
792 le_int32 run
, offset
, *si
;
794 styleRuns
.getRuns(styleRunLimits
, styleIndices
);
799 for (run
= 0; run
< styleRunCount
; run
+= 1) {
800 const LEFontInstance
*runFont
= fontRuns
->getFont(si
[0]);
801 le_int32 script
= fScriptRuns
->getValue(si
[1]);
803 while (offset
< styleRunLimits
[run
]) {
804 const LEFontInstance
*subFont
= runFont
->getSubFont(fChars
, &offset
, styleRunLimits
[run
], script
, status
);
806 if (LE_FAILURE(status
)) {
811 subFontRuns
->add(subFont
, offset
);
817 fFontRuns
= subFontRuns
;
820 LE_DELETE_ARRAY(styleIndices
);
821 LE_DELETE_ARRAY(styleRunLimits
);
824 void ParagraphLayout::computeMetrics()
826 le_int32 i
, count
= fFontRuns
->getCount();
829 for (i
= 0; i
< count
; i
+= 1) {
830 const LEFontInstance
*font
= fFontRuns
->getFont(i
);
831 le_int32 ascent
= font
->getAscent();
832 le_int32 descent
= font
->getDescent();
833 le_int32 leading
= font
->getLeading();
834 le_int32 dl
= descent
+ leading
;
836 if (ascent
> fAscent
) {
840 if (descent
> fDescent
) {
844 if (leading
> fLeading
) {
853 fLeading
= maxDL
- fDescent
;
859 const char *localeCode
;
860 le_int32 languageCode
;
863 static const LanguageMap languageMap
[] =
865 {"afr", afkLanguageCode
}, // Afrikaans
866 {"ara", araLanguageCode
}, // Arabic
867 {"asm", asmLanguageCode
}, // Assamese
868 {"bel", belLanguageCode
}, // Belarussian
869 {"ben", benLanguageCode
}, // Bengali
870 {"bod", tibLanguageCode
}, // Tibetan
871 {"bul", bgrLanguageCode
}, // Bulgarian
872 {"cat", catLanguageCode
}, // Catalan
873 {"ces", csyLanguageCode
}, // Czech
874 {"che", cheLanguageCode
}, // Chechen
875 {"cop", copLanguageCode
}, // Coptic
876 {"cym", welLanguageCode
}, // Welsh
877 {"dan", danLanguageCode
}, // Danish
878 {"deu", deuLanguageCode
}, // German
879 {"dzo", dznLanguageCode
}, // Dzongkha
880 {"ell", ellLanguageCode
}, // Greek
881 {"eng", engLanguageCode
}, // English
882 {"est", etiLanguageCode
}, // Estonian
883 {"eus", euqLanguageCode
}, // Basque
884 {"fas", farLanguageCode
}, // Farsi
885 {"fin", finLanguageCode
}, // Finnish
886 {"fra", fraLanguageCode
}, // French
887 {"gle", gaeLanguageCode
}, // Irish Gaelic
888 {"guj", gujLanguageCode
}, // Gujarati
889 {"hau", hauLanguageCode
}, // Hausa
890 {"heb", iwrLanguageCode
}, // Hebrew
891 {"hin", hinLanguageCode
}, // Hindi
892 {"hrv", hrvLanguageCode
}, // Croatian
893 {"hun", hunLanguageCode
}, // Hungarian
894 {"hye", hyeLanguageCode
}, // Armenian
895 {"ind", indLanguageCode
}, // Indonesian
896 {"ita", itaLanguageCode
}, // Italian
897 {"jpn", janLanguageCode
}, // Japanese
898 {"kan", kanLanguageCode
}, // Kannada
899 {"kas", kshLanguageCode
}, // Kashmiri
900 {"khm", khmLanguageCode
}, // Khmer
901 {"kok", kokLanguageCode
}, // Konkani
902 {"kor", korLanguageCode
}, // Korean
903 // {"mal_XXX", malLanguageCode}, // Malayalam - Traditional
904 {"mal", mlrLanguageCode
}, // Malayalam - Reformed
905 {"mar", marLanguageCode
}, // Marathi
906 {"mlt", mtsLanguageCode
}, // Maltese
907 {"mni", mniLanguageCode
}, // Manipuri
908 {"mon", mngLanguageCode
}, // Mongolian
909 {"nep", nepLanguageCode
}, // Nepali
910 {"ori", oriLanguageCode
}, // Oriya
911 {"pol", plkLanguageCode
}, // Polish
912 {"por", ptgLanguageCode
}, // Portuguese
913 {"pus", pasLanguageCode
}, // Pashto
914 {"ron", romLanguageCode
}, // Romanian
915 {"rus", rusLanguageCode
}, // Russian
916 {"san", sanLanguageCode
}, // Sanskrit
917 {"sin", snhLanguageCode
}, // Sinhalese
918 {"slk", skyLanguageCode
}, // Slovak
919 {"snd", sndLanguageCode
}, // Sindhi
920 {"slv", slvLanguageCode
}, // Slovenian
921 {"spa", espLanguageCode
}, // Spanish
922 {"sqi", sqiLanguageCode
}, // Albanian
923 {"srp", srbLanguageCode
}, // Serbian
924 {"swe", sveLanguageCode
}, // Swedish
925 {"syr", syrLanguageCode
}, // Syriac
926 {"tam", tamLanguageCode
}, // Tamil
927 {"tel", telLanguageCode
}, // Telugu
928 {"tha", thaLanguageCode
}, // Thai
929 {"tur", trkLanguageCode
}, // Turkish
930 {"urd", urdLanguageCode
}, // Urdu
931 {"yid", jiiLanguageCode
}, // Yiddish
932 // {"zhp", zhpLanguageCode}, // Chinese - Phonetic
933 {"zho", zhsLanguageCode
}, // Chinese
934 {"zho_CHN", zhsLanguageCode
}, // Chinese - China
935 {"zho_HKG", zhsLanguageCode
}, // Chinese - Hong Kong
936 {"zho_MAC", zhtLanguageCode
}, // Chinese - Macao
937 {"zho_SGP", zhsLanguageCode
}, // Chinese - Singapore
938 {"zho_TWN", zhtLanguageCode
} // Chinese - Taiwan
941 static const le_int32 languageMapCount
= ARRAY_SIZE(languageMap
);
943 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
945 char code
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
946 const char *language
= locale
->getISO3Language();
947 const char *country
= locale
->getISO3Country();
949 uprv_strcat(code
, language
);
951 if ((uprv_strcmp(language
, "zho") == 0) && country
!= NULL
) {
952 uprv_strcat(code
, "_");
953 uprv_strcat(code
, country
);
956 for (le_int32 i
= 0; i
< languageMapCount
; i
+= 1) {
957 if (uprv_strcmp(code
, languageMap
[i
].localeCode
) == 0) {
958 return languageMap
[i
].languageCode
;
962 return nullLanguageCode
;
966 // TODO - dummy implementation for right now...
967 le_int32
ParagraphLayout::getLanguageCode(const Locale
*locale
)
969 return nullLanguageCode
;
973 le_bool
ParagraphLayout::isComplex(UScriptCode script
)
975 if (script
< 0 || script
>= (UScriptCode
) scriptCodeCount
) {
979 return complexTable
[script
];
982 le_int32
ParagraphLayout::previousBreak(le_int32 charIndex
)
984 // skip over any whitespace or control characters,
985 // because they can hang in the margin.
986 while (charIndex
< fCharCount
&&
987 (u_isWhitespace(fChars
[charIndex
]) ||
988 u_iscntrl(fChars
[charIndex
]))) {
992 // Create the BreakIterator if we don't already have one
993 if (fBreakIterator
== NULL
) {
995 UCharCharacterIterator
*iter
= new UCharCharacterIterator(fChars
, fCharCount
);
996 UErrorCode status
= U_ZERO_ERROR
;
998 fBreakIterator
= BreakIterator::createLineInstance(thai
, status
);
999 fBreakIterator
->adoptText(iter
);
1002 // return the break location that's at or before
1003 // the character we stopped on. Note: if we're
1004 // on a break, the "+ 1" will cause preceding to
1006 return fBreakIterator
->preceding(charIndex
+ 1);
1009 ParagraphLayout::Line
*ParagraphLayout::computeVisualRuns()
1011 UErrorCode bidiStatus
= U_ZERO_ERROR
;
1012 le_int32 dirRunCount
, visualRun
;
1014 fVisualRunLastX
= 0;
1015 fVisualRunLastY
= 0;
1016 fFirstVisualRun
= getCharRun(fLineStart
);
1017 fLastVisualRun
= getCharRun(fLineEnd
- 1);
1019 if (fLineBidi
== NULL
) {
1020 fLineBidi
= ubidi_openSized(fCharCount
, 0, &bidiStatus
);
1023 ubidi_setLine(fParaBidi
, fLineStart
, fLineEnd
, fLineBidi
, &bidiStatus
);
1024 dirRunCount
= ubidi_countRuns(fLineBidi
, &bidiStatus
);
1026 Line
*line
= new Line();
1028 for (visualRun
= 0; visualRun
< dirRunCount
; visualRun
+= 1) {
1029 le_int32 relStart
, run
, runLength
;
1030 UBiDiDirection runDirection
= ubidi_getVisualRun(fLineBidi
, visualRun
, &relStart
, &runLength
);
1031 le_int32 runStart
= fLineStart
+ relStart
;
1032 le_int32 runEnd
= runStart
+ runLength
- 1;
1033 le_int32 firstRun
= getCharRun(runStart
);
1034 le_int32 lastRun
= getCharRun(runEnd
);
1035 le_int32 startRun
= (runDirection
== UBIDI_LTR
)? firstRun
: lastRun
;
1036 le_int32 stopRun
= (runDirection
== UBIDI_LTR
)? lastRun
+ 1 : firstRun
- 1;
1037 le_int32 dir
= (runDirection
== UBIDI_LTR
)? 1 : -1;
1039 for (run
= startRun
; run
!= stopRun
; run
+= dir
) {
1040 le_int32 firstChar
= (run
== firstRun
)? runStart
: fStyleRunInfo
[run
].runBase
;
1041 le_int32 lastChar
= (run
== lastRun
)? runEnd
: fStyleRunInfo
[run
].runLimit
- 1;
1043 appendRun(line
, run
, firstChar
, lastChar
);
1050 void ParagraphLayout::appendRun(ParagraphLayout::Line
*line
, le_int32 run
, le_int32 firstChar
, le_int32 lastChar
)
1052 le_int32 glyphBase
= fStyleRunInfo
[run
].glyphBase
;
1053 le_int32 inGlyph
, outGlyph
;
1055 // Get the glyph indices for all the characters between firstChar and lastChar,
1056 // make the minimum one be leftGlyph and the maximum one be rightGlyph.
1057 // (need to do this to handle local reorderings like Indic left matras)
1058 le_int32 leftGlyph
= fGlyphCount
;
1059 le_int32 rightGlyph
= -1;
1062 for (ch
= firstChar
; ch
<= lastChar
; ch
+= 1) {
1063 le_int32 minGlyph
= fCharToMinGlyphMap
[ch
];
1064 le_int32 maxGlyph
= fCharToMaxGlyphMap
[ch
];
1066 if (minGlyph
< leftGlyph
) {
1067 leftGlyph
= minGlyph
;
1070 if (maxGlyph
> rightGlyph
) {
1071 rightGlyph
= maxGlyph
;
1075 if ((fStyleRunInfo
[run
].level
& 1) != 0) {
1076 le_int32 swap
= rightGlyph
;
1077 le_int32 last
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
1079 // Here, we want to remove the glyphBase bias...
1080 rightGlyph
= last
- leftGlyph
;
1081 leftGlyph
= last
- swap
;
1083 rightGlyph
-= glyphBase
;
1084 leftGlyph
-= glyphBase
;
1087 // Set the position bias for the glyphs. If we're at the start of
1088 // a line, we want the first glyph to be at x = 0, even if it comes
1089 // from the middle of a layout. If we've got a right-to-left run, we
1090 // want the left-most glyph to start at the final x position of the
1091 // previous run, even though this glyph may be in the middle of the
1093 fVisualRunLastX
-= fStyleRunInfo
[run
].positions
[leftGlyph
* 2];
1095 // Make rightGlyph be the glyph just to the right of
1099 UBiDiDirection direction
= ((fStyleRunInfo
[run
].level
& 1) == 0)? UBIDI_LTR
: UBIDI_RTL
;
1100 le_int32 glyphCount
= rightGlyph
- leftGlyph
;
1101 LEGlyphID
*glyphs
= LE_NEW_ARRAY(LEGlyphID
, glyphCount
);
1102 float *positions
= LE_NEW_ARRAY(float, glyphCount
* 2 + 2);
1103 le_int32
*glyphToCharMap
= LE_NEW_ARRAY(le_int32
, glyphCount
);
1105 LE_ARRAY_COPY(glyphs
, &fStyleRunInfo
[run
].glyphs
[leftGlyph
], glyphCount
);
1107 for (outGlyph
= 0, inGlyph
= leftGlyph
* 2; inGlyph
<= rightGlyph
* 2; inGlyph
+= 2, outGlyph
+= 2) {
1108 positions
[outGlyph
] = fStyleRunInfo
[run
].positions
[inGlyph
] + fVisualRunLastX
;
1109 positions
[outGlyph
+ 1] = fStyleRunInfo
[run
].positions
[inGlyph
+ 1] + fVisualRunLastY
;
1112 // Save the ending position of this run
1113 // to use for the start of the next run
1114 fVisualRunLastX
= positions
[outGlyph
- 2];
1115 fVisualRunLastY
= positions
[outGlyph
- 1];
1117 if ((fStyleRunInfo
[run
].level
& 1) == 0) {
1118 for (outGlyph
= 0, inGlyph
= leftGlyph
; inGlyph
< rightGlyph
; inGlyph
+= 1, outGlyph
+= 1) {
1119 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[glyphBase
+ inGlyph
];
1122 // Because fGlyphToCharMap is stored in logical order to facilitate line breaking,
1123 // we need to map the physical glyph indices to logical indices while we copy the
1124 // character indices.
1125 le_int32 base
= glyphBase
+ fStyleRunInfo
[run
].glyphCount
- 1;
1127 for (outGlyph
= 0, inGlyph
= leftGlyph
; inGlyph
< rightGlyph
; inGlyph
+= 1, outGlyph
+= 1) {
1128 glyphToCharMap
[outGlyph
] = fGlyphToCharMap
[base
- inGlyph
];
1132 line
->append(fStyleRunInfo
[run
].font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1135 le_int32
ParagraphLayout::getCharRun(le_int32 charIndex
)
1137 if (charIndex
< 0 || charIndex
> fCharCount
) {
1143 // NOTE: as long as fStyleRunLimits is well-formed
1144 // the above range check guarantees that we'll never
1145 // fall off the end of the array.
1147 while (charIndex
>= fStyleRunLimits
[run
]) {
1155 const char ParagraphLayout::Line::fgClassID
= 0;
1157 #define INITIAL_RUN_CAPACITY 4
1158 #define RUN_CAPACITY_GROW_LIMIT 16
1160 ParagraphLayout::Line::~Line()
1164 for (i
= 0; i
< fRunCount
; i
+= 1) {
1168 LE_DELETE_ARRAY(fRuns
);
1171 le_int32
ParagraphLayout::Line::getAscent() const
1174 ((ParagraphLayout::Line
*)this)->computeMetrics();
1180 le_int32
ParagraphLayout::Line::getDescent() const
1183 ((ParagraphLayout::Line
*)this)->computeMetrics();
1189 le_int32
ParagraphLayout::Line::getLeading() const
1192 ((ParagraphLayout::Line
*)this)->computeMetrics();
1198 le_int32
ParagraphLayout::Line::getWidth() const
1200 const VisualRun
*lastRun
= getVisualRun(fRunCount
- 1);
1202 if (lastRun
== NULL
) {
1206 le_int32 glyphCount
= lastRun
->getGlyphCount();
1207 const float *positions
= lastRun
->getPositions();
1209 return (le_int32
) positions
[glyphCount
* 2];
1212 const ParagraphLayout::VisualRun
*ParagraphLayout::Line::getVisualRun(le_int32 runIndex
) const
1214 if (runIndex
< 0 || runIndex
>= fRunCount
) {
1218 return fRuns
[runIndex
];
1221 void ParagraphLayout::Line::append(const LEFontInstance
*font
, UBiDiDirection direction
, le_int32 glyphCount
,
1222 const LEGlyphID glyphs
[], const float positions
[], const le_int32 glyphToCharMap
[])
1224 if (fRunCount
>= fRunCapacity
) {
1225 if (fRunCapacity
== 0) {
1226 fRunCapacity
= INITIAL_RUN_CAPACITY
;
1227 fRuns
= LE_NEW_ARRAY(ParagraphLayout::VisualRun
*, fRunCapacity
);
1229 fRunCapacity
+= (fRunCapacity
< RUN_CAPACITY_GROW_LIMIT
? fRunCapacity
: RUN_CAPACITY_GROW_LIMIT
);
1230 fRuns
= (ParagraphLayout::VisualRun
**) LE_GROW_ARRAY(fRuns
, fRunCapacity
);
1234 fRuns
[fRunCount
++] = new ParagraphLayout::VisualRun(font
, direction
, glyphCount
, glyphs
, positions
, glyphToCharMap
);
1237 void ParagraphLayout::Line::computeMetrics()
1241 for (le_int32 i
= 0; i
< fRunCount
; i
+= 1) {
1242 le_int32 ascent
= fRuns
[i
]->getAscent();
1243 le_int32 descent
= fRuns
[i
]->getDescent();
1244 le_int32 leading
= fRuns
[i
]->getLeading();
1245 le_int32 dl
= descent
+ leading
;
1247 if (ascent
> fAscent
) {
1251 if (descent
> fDescent
) {
1255 if (leading
> fLeading
) {
1264 fLeading
= maxDL
- fDescent
;
1267 const char ParagraphLayout::VisualRun::fgClassID
= 0;
1269 ParagraphLayout::VisualRun::~VisualRun()
1271 LE_DELETE_ARRAY(fGlyphToCharMap
);
1272 LE_DELETE_ARRAY(fPositions
);
1273 LE_DELETE_ARRAY(fGlyphs
);