2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/devel-api/text-abstraction/font-client.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
48 #if defined(DEBUG_ENABLED)
49 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
52 const float MAX_FLOAT = std::numeric_limits<float>::max();
53 const CharacterDirection LTR = false;
54 const CharacterDirection RTL = !LTR;
55 const float LINE_SPACING= 0.f;
57 inline bool isEmptyLineAtLast( const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line )
59 return ( (*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End() );
65 * @brief Stores temporary layout info of the line.
73 numberOfCharacters{ 0u },
74 ascender{ -MAX_FLOAT },
75 descender{ MAX_FLOAT },
78 previousAdvance{ 0.f },
80 whiteSpaceLengthEndOfLine{ 0.f },
92 numberOfCharacters = 0u;
93 ascender = -MAX_FLOAT;
94 descender = MAX_FLOAT;
98 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
99 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
100 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
101 Length numberOfCharacters; ///< The number of characters which fit in one line.
102 float ascender; ///< The maximum ascender of all fonts in the line.
103 float descender; ///< The minimum descender of all fonts in the line.
104 float lineSpacing; ///< The line spacing
105 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
106 float previousAdvance; ///< The advance of the previous glyph.
107 float length; ///< The current length of the line.
108 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
109 CharacterDirection direction;
112 struct LayoutBidiParameters
116 paragraphDirection = LTR;
117 bidiParagraphIndex = 0u;
119 isBidirectional = false;
122 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
123 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
124 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
125 bool isBidirectional = false; ///< Whether the text is bidirectional.
131 : mLayout{ Layout::Engine::SINGLE_LINE_BOX },
133 mDefaultLineSpacing{ LINE_SPACING }
138 * @brief Updates the line ascender and descender with the metrics of a new font.
140 * @param[in] glyphMetrics The metrics of the new font.
141 * @param[in,out] lineLayout The line layout.
143 void UpdateLineHeight( const GlyphMetrics& glyphMetrics, LineLayout& lineLayout )
145 Text::FontMetrics fontMetrics;
146 if( 0u != glyphMetrics.fontId )
148 mMetrics->GetFontMetrics( glyphMetrics.fontId, fontMetrics );
152 fontMetrics.ascender = glyphMetrics.fontHeight;
153 fontMetrics.descender = 0.f;
154 fontMetrics.height = fontMetrics.ascender;
155 fontMetrics.underlinePosition = 0.f;
156 fontMetrics.underlineThickness = 1.f;
159 // Sets the maximum ascender.
160 lineLayout.ascender = std::max( lineLayout.ascender, fontMetrics.ascender );
162 // Sets the minimum descender.
163 lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender );
165 // set the line spacing
166 lineLayout.lineSpacing = mDefaultLineSpacing;
170 * @brief Merges a temporary line layout into the line layout.
172 * @param[in,out] lineLayout The line layout.
173 * @param[in] tmpLineLayout A temporary line layout.
175 void MergeLineLayout( LineLayout& lineLayout,
176 const LineLayout& tmpLineLayout )
178 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
179 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
181 lineLayout.penX = tmpLineLayout.penX;
182 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
184 lineLayout.length = tmpLineLayout.length;
185 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
187 // Sets the maximum ascender.
188 lineLayout.ascender = std::max( lineLayout.ascender, tmpLineLayout.ascender );
190 // Sets the minimum descender.
191 lineLayout.descender = std::min( lineLayout.descender, tmpLineLayout.descender );
194 void LayoutRightToLeft( const Parameters& parameters,
195 const BidirectionalLineInfoRun& bidirectionalLineInfo,
197 float& whiteSpaceLengthEndOfLine )
199 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
200 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
201 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
202 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
204 const float outlineWidth = static_cast<float>( parameters.textModel->GetOutlineWidth() );
205 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
207 CharacterIndex characterLogicalIndex = 0u;
208 CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
210 if( RTL == bidirectionalLineInfo.direction )
212 while( TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) ) )
214 const GlyphInfo& glyphInfo = *( glyphsBuffer + *( charactersToGlyphsBuffer + characterVisualIndex ) );
216 whiteSpaceLengthEndOfLine += glyphInfo.advance;
218 ++characterLogicalIndex;
219 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
223 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
225 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
226 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
227 lastGlyphOfParagraphPlusOne,
228 charactersPerGlyphBuffer );
230 GlyphMetrics glyphMetrics;
231 GetGlyphsMetrics( glyphIndex,
232 numberOfGLyphsInGroup,
237 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
239 // Traverses the characters of the right to left paragraph.
240 for( ; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters; )
242 // Convert the character in the logical order into the character in the visual order.
243 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
244 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) );
246 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
248 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
249 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
250 lastGlyphOfParagraphPlusOne,
251 charactersPerGlyphBuffer );
253 characterLogicalIndex += *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
255 GlyphMetrics glyphMetrics;
256 GetGlyphsMetrics( glyphIndex,
257 numberOfGLyphsInGroup,
264 if( RTL == bidirectionalLineInfo.direction )
266 length += glyphMetrics.advance;
270 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
272 penX += glyphMetrics.advance;
276 if( LTR == bidirectionalLineInfo.direction )
278 whiteSpaceLengthEndOfLine = 0.f;
280 length = std::max( length, penX + glyphMetrics.xBearing + glyphMetrics.width );
281 penX += ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
286 void ReorderBiDiLayout( const Parameters& parameters,
287 LayoutBidiParameters& bidiParameters,
288 const LineLayout& currentLineLayout,
289 LineLayout& lineLayout,
290 bool breakInCharacters )
292 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
294 // The last glyph to be laid-out.
295 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
297 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
299 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
300 if( ( lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex ) &&
301 ( lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters ) )
303 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
305 // Sets the visual to logical map tables needed to reorder the text.
306 ReorderLine( bidirectionalParagraphInfo,
307 bidirectionalLinesInfo,
308 bidiParameters.bidiLineIndex,
309 lineLayout.characterIndex,
310 lineLayout.numberOfCharacters,
311 bidiParameters.paragraphDirection );
313 // Recalculate the length of the line and update the layout.
314 const BidirectionalLineInfoRun& bidirectionalLineInfo = *( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
316 if( !bidirectionalLineInfo.isIdentity )
319 float whiteSpaceLengthEndOfLine = 0.f;
320 LayoutRightToLeft( parameters,
321 bidirectionalLineInfo,
323 whiteSpaceLengthEndOfLine );
325 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
326 if( !Equals( length, lineLayout.length ) )
328 const bool isMultiline = mLayout == MULTI_LINE_BOX;
330 if( isMultiline && ( length > parameters.boundingBox.width ) )
332 if( breakInCharacters || ( isMultiline && ( 0u == currentLineLayout.numberOfGlyphs ) ) )
334 // The word doesn't fit in one line. It has to be split by character.
336 // Remove the last laid out glyph(s) as they doesn't fit.
337 for( GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex; )
339 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
340 lastGlyphOfParagraphPlusOne,
341 charactersPerGlyphBuffer );
343 const Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
345 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
346 lineLayout.numberOfCharacters -= numberOfCharacters;
348 AdjustLayout( parameters,
350 bidirectionalParagraphInfo,
353 if( lineLayout.length < parameters.boundingBox.width )
358 if( glyphIndex < numberOfGLyphsInGroup )
360 // avoids go under zero for an unsigned int.
364 glyphIndex -= numberOfGLyphsInGroup;
369 lineLayout = currentLineLayout;
371 AdjustLayout( parameters,
373 bidirectionalParagraphInfo,
379 lineLayout.length = std::max( length, lineLayout.length );
386 void AdjustLayout( const Parameters& parameters,
387 LayoutBidiParameters& bidiParameters,
388 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
389 LineLayout& lineLayout )
391 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
393 // Remove current reordered line.
394 bidirectionalLinesInfo.Erase( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
396 // Re-build the conversion table without the removed glyphs.
397 ReorderLine( bidirectionalParagraphInfo,
398 bidirectionalLinesInfo,
399 bidiParameters.bidiLineIndex,
400 lineLayout.characterIndex,
401 lineLayout.numberOfCharacters,
402 bidiParameters.paragraphDirection );
404 const BidirectionalLineInfoRun& bidirectionalLineInfo = *( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
407 float whiteSpaceLengthEndOfLine = 0.f;
408 LayoutRightToLeft( parameters,
409 bidirectionalLineInfo,
411 whiteSpaceLengthEndOfLine );
413 lineLayout.length = length;
414 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
418 * Retrieves the line layout for a given box width.
420 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
421 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
422 * to reorder the line and recalculate its length.
425 * @param[in] parameters The layout parameters.
426 * @param[] bidiParameters Bidirectional info for the current line.
427 * @param[out] lineLayout The line layout.
428 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
430 void GetLineLayoutForBox( const Parameters& parameters,
431 LayoutBidiParameters& bidiParameters,
432 LineLayout& lineLayout,
433 bool completelyFill )
435 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
436 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
438 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
439 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
440 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
441 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
442 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
444 const float outlineWidth = static_cast<float>( parameters.textModel->GetOutlineWidth() );
445 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
447 const bool isMultiline = mLayout == MULTI_LINE_BOX;
448 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD;
450 // The last glyph to be laid-out.
451 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
453 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
454 // In the case the line starts with a right to left character, if the width is longer than the advance,
455 // the difference needs to be added to the line length.
457 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
458 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
459 lastGlyphOfParagraphPlusOne,
460 charactersPerGlyphBuffer );
462 GlyphMetrics glyphMetrics;
463 GetGlyphsMetrics( lineLayout.glyphIndex,
464 numberOfGLyphsInGroup,
469 // Set the direction of the first character of the line.
470 lineLayout.characterIndex = *( glyphsToCharactersBuffer + lineLayout.glyphIndex );
472 // Stores temporary line layout which has not been added to the final line layout.
473 LineLayout tmpLineLayout;
475 // Initialize the start point.
477 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
478 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
479 // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined.
480 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
482 // Calculate the line height if there is no characters.
483 FontId lastFontId = glyphMetrics.fontId;
484 UpdateLineHeight( glyphMetrics, tmpLineLayout );
486 bool oneWordLaidOut = false;
488 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
489 glyphIndex < lastGlyphOfParagraphPlusOne; )
491 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
493 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
494 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
495 lastGlyphOfParagraphPlusOne,
496 charactersPerGlyphBuffer );
498 GlyphMetrics glyphMetrics;
499 GetGlyphsMetrics( glyphIndex,
500 numberOfGLyphsInGroup,
505 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
507 // Check if the font of the current glyph is the same of the previous one.
508 // If it's different the ascender and descender need to be updated.
509 if( lastFontId != glyphMetrics.fontId )
511 UpdateLineHeight( glyphMetrics, tmpLineLayout );
512 lastFontId = glyphMetrics.fontId;
515 // Get the character indices for the current glyph. The last character index is needed
516 // because there are glyphs formed by more than one character but their break info is
517 // given only for the last character.
518 const Length charactersPerGlyph = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
519 const bool hasCharacters = charactersPerGlyph > 0u;
520 const CharacterIndex characterFirstIndex = *( glyphsToCharactersBuffer + glyphIndex );
521 const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
523 // Get the line break info for the current character.
524 const LineBreakInfo lineBreakInfo = hasCharacters ? *( lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
526 // Increase the number of characters.
527 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
529 // Increase the number of glyphs.
530 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
532 // Check whether is a white space.
533 const Character character = *( textBuffer + characterFirstIndex );
534 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
536 // Calculate the length of the line.
538 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
539 const float previousTmpPenX = tmpLineLayout.penX;
540 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
541 const float previousTmpLength = tmpLineLayout.length;
542 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
546 // Add the length to the length of white spaces at the end of the line.
547 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
551 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
552 tmpLineLayout.previousAdvance = ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
554 tmpLineLayout.length = std::max( tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width );
556 // Clear the white space length at the end of the line.
557 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
560 // Check if the accumulated length fits in the width of the box.
561 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
562 ( tmpLineLayout.length > parameters.boundingBox.width ) )
564 // Current word does not fit in the box's width.
565 if( !oneWordLaidOut || completelyFill )
567 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
569 // The word doesn't fit in the control's width. It needs to be split by character.
570 if( tmpLineLayout.numberOfGlyphs > 0u )
572 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
573 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
575 tmpLineLayout.penX = previousTmpPenX;
576 tmpLineLayout.previousAdvance = previousTmpAdvance;
577 tmpLineLayout.length = previousTmpLength;
578 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
581 // Add part of the word to the line layout.
582 MergeLineLayout( lineLayout, tmpLineLayout );
586 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
589 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
591 // Reorder the RTL line.
592 if( bidiParameters.isBidirectional )
594 ReorderBiDiLayout( parameters,
604 if( ( isMultiline || isLastGlyph ) &&
605 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
607 LineLayout currentLineLayout = lineLayout;
609 // Must break the line. Update the line layout and return.
610 MergeLineLayout( lineLayout, tmpLineLayout );
612 // Reorder the RTL line.
613 if( bidiParameters.isBidirectional )
615 ReorderBiDiLayout( parameters,
622 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
623 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
629 ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
631 oneWordLaidOut = isWordLaidOut;
632 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
634 // Current glyph is the last one of the current word.
635 // Add the temporal layout to the current one.
636 MergeLineLayout( lineLayout, tmpLineLayout );
638 tmpLineLayout.Clear();
641 glyphIndex += numberOfGLyphsInGroup;
644 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
647 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
648 Length numberOfGlyphs,
650 float interGlyphExtraAdvance,
651 Vector2* glyphPositionsBuffer )
653 // Traverse the glyphs and set the positions.
655 // Check if the x bearing of the first character is negative.
656 // If it has a negative x bearing, it will exceed the boundaries of the actor,
657 // so the penX position needs to be moved to the right.
659 const GlyphInfo& glyph = *glyphsBuffer;
660 float penX = -glyph.xBearing + mCursorWidth + outlineWidth;
662 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
664 const GlyphInfo& glyph = *( glyphsBuffer + i );
665 Vector2& position = *( glyphPositionsBuffer + i );
667 position.x = std::roundf( penX + glyph.xBearing );
668 position.y = -glyph.yBearing;
670 penX += ( glyph.advance + interGlyphExtraAdvance );
674 void SetGlyphPositions( const Parameters& layoutParameters,
675 Vector2* glyphPositionsBuffer,
676 LayoutBidiParameters& layoutBidiParameters,
677 const LineLayout& layout )
679 const Character* const textBuffer = layoutParameters.textModel->mLogicalModel->mText.Begin();
680 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
681 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
682 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
683 const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
685 CharacterIndex characterLogicalIndex = 0u;
686 CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
689 while( TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) ) )
691 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
692 const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
694 Vector2& position = *( glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex );
696 position.y = -glyph.yBearing;
698 penX += glyph.advance;
700 ++characterLogicalIndex;
701 characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
704 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
705 const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
707 penX += -glyph.xBearing;
709 // Traverses the characters of the right to left paragraph.
710 for( ; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
711 ++characterLogicalIndex )
713 // Convert the character in the logical order into the character in the visual order.
714 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
716 // Get the number of glyphs of the character.
717 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterVisualIndex );
719 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
721 // Convert the character in the visual order into the glyph in the visual order.
722 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex ) + index;
724 DALI_ASSERT_DEBUG( glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count() );
726 const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
727 Vector2& position = *( glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex );
729 position.x = std::round( penX + glyph.xBearing );
730 position.y = -glyph.yBearing;
732 penX += ( glyph.advance + layoutParameters.interGlyphExtraAdvance );
738 * @brief Resizes the line buffer.
740 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
741 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
742 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
743 * @param[in] updateCurrentBuffer Whether the layout is updated.
745 * @return Pointer to either lines or newLines.
747 LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
748 Vector<LineRun>& newLines,
749 Length& linesCapacity,
750 bool updateCurrentBuffer )
752 LineRun* linesBuffer = nullptr;
753 // Reserve more space for the next lines.
755 if( updateCurrentBuffer )
757 newLines.Resize( linesCapacity );
758 linesBuffer = newLines.Begin();
762 lines.Resize( linesCapacity );
763 linesBuffer = lines.Begin();
770 * Ellipsis a line if it exceeds the width's of the bounding box.
772 * @param[in] layoutParameters The parameters needed to layout the text.
773 * @param[in] layout The line layout.
774 * @param[in,out] layoutSize The text's layout size.
775 * @param[in,out] linesBuffer Pointer to the line's buffer.
776 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
777 * @param[in,out] numberOfLines The number of laid-out lines.
778 * @param[in] penY The vertical layout position.
779 * @param[in] currentParagraphDirection The current paragraph's direction.
780 * @param[in,out] isAutoScrollEnabled If the isAutoScrollEnabled is true and the height of the text exceeds the boundaries of the control the text is elided and the isAutoScrollEnabled is set to false to disable the autoscroll
782 * return Whether the line is ellipsized.
784 bool EllipsisLine( const Parameters& layoutParameters,
785 LayoutBidiParameters& layoutBidiParameters,
786 const LineLayout& layout,
788 LineRun* linesBuffer,
789 Vector2* glyphPositionsBuffer,
790 Length& numberOfLines,
792 bool& isAutoScrollEnabled )
794 const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) :
795 ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
796 ( ( mLayout == SINGLE_LINE_BOX ) &&
797 ( layout.length > layoutParameters.boundingBox.width ) ) );
801 isAutoScrollEnabled = false;
802 // Do not layout more lines if ellipsis is enabled.
804 // The last line needs to be completely filled with characters.
805 // Part of a word may be used.
807 LineRun* lineRun = nullptr;
808 LineLayout ellipsisLayout;
809 if( 0u != numberOfLines )
811 // Get the last line and layout it again with the 'completelyFill' flag to true.
812 lineRun = linesBuffer + ( numberOfLines - 1u );
814 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
816 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
820 // At least there is space reserved for one line.
821 lineRun = linesBuffer;
823 lineRun->glyphRun.glyphIndex = 0u;
824 ellipsisLayout.glyphIndex = 0u;
829 GetLineLayoutForBox( layoutParameters,
830 layoutBidiParameters,
834 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
835 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
836 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
837 lineRun->width = ellipsisLayout.length;
838 lineRun->extraLength = std::ceil( ellipsisLayout.whiteSpaceLengthEndOfLine );
839 lineRun->ascender = ellipsisLayout.ascender;
840 lineRun->descender = ellipsisLayout.descender;
841 lineRun->ellipsis = true;
843 layoutSize.width = layoutParameters.boundingBox.width;
844 if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
846 layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
849 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
850 const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
852 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
854 if( layoutBidiParameters.isBidirectional )
856 layoutBidiParameters.bidiLineIndex = 0u;
857 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
858 endIt = bidirectionalLinesInfo.End();
860 ++it, ++layoutBidiParameters.bidiLineIndex )
862 const BidirectionalLineInfoRun& run = *it;
864 if( ellipsisLayout.characterIndex == run.characterRun.characterIndex )
866 // Found where to insert the bidi line info.
872 const BidirectionalLineInfoRun* const bidirectionalLineInfo = ( layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty() ) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
874 if( ( nullptr != bidirectionalLineInfo ) &&
875 !bidirectionalLineInfo->isIdentity &&
876 ( ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex ) )
878 lineRun->direction = RTL;
879 SetGlyphPositions( layoutParameters,
880 glyphPositionsBuffer,
881 layoutBidiParameters,
886 lineRun->direction = LTR;
887 SetGlyphPositions( glyphsBuffer + lineRun->glyphRun.glyphIndex,
888 ellipsisLayout.numberOfGlyphs,
890 layoutParameters.interGlyphExtraAdvance,
891 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
899 * @brief Updates the text layout with a new laid-out line.
901 * @param[in] layoutParameters The parameters needed to layout the text.
902 * @param[in] layout The line layout.
903 * @param[in,out] layoutSize The text's layout size.
904 * @param[in,out] linesBuffer Pointer to the line's buffer.
905 * @param[in] index Index to the vector of glyphs.
906 * @param[in,out] numberOfLines The number of laid-out lines.
907 * @param[in] isLastLine Whether the laid-out line is the last one.
909 void UpdateTextLayout( const Parameters& layoutParameters,
910 const LineLayout& layout,
912 LineRun* linesBuffer,
914 Length& numberOfLines,
917 LineRun& lineRun = *( linesBuffer + numberOfLines );
920 lineRun.glyphRun.glyphIndex = index;
921 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
922 lineRun.characterRun.characterIndex = layout.characterIndex;
923 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
924 lineRun.lineSpacing = mDefaultLineSpacing;
926 lineRun.width = layout.length;
927 lineRun.extraLength = std::ceil( layout.whiteSpaceLengthEndOfLine );
930 // Rounds upward to avoid a non integer size.
931 lineRun.width = std::ceil( lineRun.width );
933 lineRun.ascender = layout.ascender;
934 lineRun.descender = layout.descender;
935 lineRun.direction = layout.direction;
936 lineRun.ellipsis = false;
938 // Update the actual size.
939 if( lineRun.width > layoutSize.width )
941 layoutSize.width = lineRun.width;
944 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
948 * @brief Updates the text layout with the last laid-out line.
950 * @param[in] layoutParameters The parameters needed to layout the text.
951 * @param[in] characterIndex The character index of the line.
952 * @param[in] glyphIndex The glyph index of the line.
953 * @param[in,out] layoutSize The text's layout size.
954 * @param[in,out] linesBuffer Pointer to the line's buffer.
955 * @param[in,out] numberOfLines The number of laid-out lines.
957 void UpdateTextLayout( const Parameters& layoutParameters,
958 CharacterIndex characterIndex,
959 GlyphIndex glyphIndex,
961 LineRun* linesBuffer,
962 Length& numberOfLines )
964 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
966 // Need to add a new line with no characters but with height to increase the layoutSize.height
967 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
969 Text::FontMetrics fontMetrics;
970 if( 0u != glyphInfo.fontId )
972 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
975 LineRun& lineRun = *( linesBuffer + numberOfLines );
978 lineRun.glyphRun.glyphIndex = glyphIndex;
979 lineRun.glyphRun.numberOfGlyphs = 0u;
980 lineRun.characterRun.characterIndex = characterIndex;
981 lineRun.characterRun.numberOfCharacters = 0u;
983 lineRun.ascender = fontMetrics.ascender;
984 lineRun.descender = fontMetrics.descender;
985 lineRun.extraLength = 0.f;
986 lineRun.alignmentOffset = 0.f;
987 lineRun.direction = LTR;
988 lineRun.ellipsis = false;
989 lineRun.lineSpacing = mDefaultLineSpacing;
991 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
995 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
997 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
998 * @param[in,out] layoutSize The text's layout size.
1000 void UpdateLayoutSize( const Vector<LineRun>& lines,
1003 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
1004 endIt = lines.End();
1008 const LineRun& line = *it;
1010 if( line.width > layoutSize.width )
1012 layoutSize.width = line.width;
1015 layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
1020 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1022 * @param[in] layoutParameters The parameters needed to layout the text.
1023 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1024 * @param[in] characterOffset The offset to be added to the runs of characters.
1025 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1027 void UpdateLineIndexOffsets( const Parameters& layoutParameters,
1028 Vector<LineRun>& lines,
1029 Length characterOffset,
1030 Length glyphOffset )
1032 // Update the glyph and character runs.
1033 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1034 endIt = lines.End();
1038 LineRun& line = *it;
1040 line.glyphRun.glyphIndex = glyphOffset;
1041 line.characterRun.characterIndex = characterOffset;
1043 glyphOffset += line.glyphRun.numberOfGlyphs;
1044 characterOffset += line.characterRun.numberOfCharacters;
1048 bool LayoutText( Parameters& layoutParameters,
1050 bool elideTextEnabled,
1051 bool& isAutoScrollEnabled )
1053 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
1054 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
1056 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1058 if( 0u == layoutParameters.numberOfGlyphs )
1060 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1061 if( layoutParameters.isLastNewParagraph )
1063 Length numberOfLines = lines.Count();
1064 if( 0u != numberOfLines )
1066 const LineRun& lastLine = *( lines.End() - 1u );
1068 if( 0u != lastLine.characterRun.numberOfCharacters )
1070 // Need to add a new line with no characters but with height to increase the layoutSize.height
1072 Initialize( newLine );
1073 lines.PushBack( newLine );
1075 UpdateTextLayout( layoutParameters,
1076 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1077 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1085 // Calculates the layout size.
1086 UpdateLayoutSize( lines,
1089 // Rounds upward to avoid a non integer size.
1090 layoutSize.height = std::ceil( layoutSize.height );
1092 // Nothing else do if there are no glyphs to layout.
1096 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1097 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1098 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1100 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1101 // This extra line needs to be removed.
1102 if( 0u != lines.Count() )
1104 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1106 if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
1107 ( lastGlyphPlusOne == totalNumberOfGlyphs ) )
1109 lines.Remove( lastLine );
1113 // Retrieve BiDi info.
1114 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1116 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1117 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1118 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1120 // Set the layout bidirectional paramters.
1121 LayoutBidiParameters layoutBidiParameters;
1123 // Whether the layout is being updated or set from scratch.
1124 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1126 Vector2* glyphPositionsBuffer = nullptr;
1127 Vector<Vector2> newGlyphPositions;
1129 LineRun* linesBuffer = nullptr;
1130 Vector<LineRun> newLines;
1132 // Estimate the number of lines.
1133 Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
1134 Length numberOfLines = 0u;
1136 if( updateCurrentBuffer )
1138 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
1139 glyphPositionsBuffer = newGlyphPositions.Begin();
1141 newLines.Resize( linesCapacity );
1142 linesBuffer = newLines.Begin();
1146 glyphPositionsBuffer = glyphPositions.Begin();
1148 lines.Resize( linesCapacity );
1149 linesBuffer = lines.Begin();
1152 float penY = CalculateLineOffset( lines,
1153 layoutParameters.startLineIndex );
1154 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
1156 layoutBidiParameters.Clear();
1158 if( hasBidiParagraphs )
1160 const CharacterIndex startCharacterIndex = *( glyphsToCharactersBuffer + index );
1162 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1163 endIt = bidirectionalParagraphsInfo.End();
1165 ++it, ++layoutBidiParameters.bidiParagraphIndex )
1167 const BidirectionalParagraphInfoRun& run = *it;
1169 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1171 if( lastCharacterIndex <= startCharacterIndex )
1173 // Do not process, the paragraph has already been processed.
1177 if( startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex )
1179 layoutBidiParameters.paragraphDirection = run.direction;
1180 layoutBidiParameters.isBidirectional = true;
1183 // Has already been found.
1187 if( layoutBidiParameters.isBidirectional )
1189 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1190 endIt = bidirectionalLinesInfo.End();
1192 ++it, ++layoutBidiParameters.bidiLineIndex )
1194 const BidirectionalLineInfoRun& run = *it;
1196 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1198 if( lastCharacterIndex <= startCharacterIndex )
1204 if( startCharacterIndex < lastCharacterIndex )
1206 // Found where to insert the bidi line info.
1213 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1215 // Get the layout for the line.
1217 layout.direction = layoutBidiParameters.paragraphDirection;
1218 layout.glyphIndex = index;
1219 GetLineLayoutForBox( layoutParameters,
1220 layoutBidiParameters,
1224 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
1225 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
1226 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
1227 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
1228 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
1230 if( 0u == layout.numberOfGlyphs )
1232 // The width is too small and no characters are laid-out.
1233 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
1235 lines.Resize( numberOfLines );
1237 // Rounds upward to avoid a non integer size.
1238 layoutSize.height = std::ceil( layoutSize.height );
1243 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
1245 penY += layout.ascender;
1247 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
1249 bool ellipsis = false;
1250 if( elideTextEnabled )
1252 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1254 // Does the ellipsis of the last line.
1255 ellipsis = EllipsisLine( layoutParameters,
1256 layoutBidiParameters,
1260 glyphPositionsBuffer,
1263 isAutoScrollEnabled );
1268 // No more lines to layout.
1273 // Whether the last line has been laid-out.
1274 const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
1276 if( numberOfLines == linesCapacity )
1279 // Reserve more space for the next lines.
1280 linesBuffer = ResizeLinesBuffer( lines,
1283 updateCurrentBuffer );
1286 // Updates the current text's layout with the line's layout.
1287 UpdateTextLayout( layoutParameters,
1295 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
1297 if( ( nextIndex == totalNumberOfGlyphs ) &&
1298 layoutParameters.isLastNewParagraph &&
1299 ( mLayout == MULTI_LINE_BOX ) )
1301 // The last character of the text is a new paragraph character.
1302 // An extra line with no characters is added to increase the text's height
1303 // in order to place the cursor.
1305 if( numberOfLines == linesCapacity )
1307 // Reserve more space for the next lines.
1308 linesBuffer = ResizeLinesBuffer( lines,
1311 updateCurrentBuffer );
1314 UpdateTextLayout( layoutParameters,
1315 layout.characterIndex + layout.numberOfCharacters,
1316 index + layout.numberOfGlyphs,
1320 } // whether to add a last line.
1322 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1323 const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
1325 const BidirectionalLineInfoRun* const bidirectionalLineInfo = ( layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty() ) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1327 if( ( nullptr != bidirectionalLineInfo ) &&
1328 !bidirectionalLineInfo->isIdentity &&
1329 ( layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex ) )
1331 SetGlyphPositions( layoutParameters,
1332 glyphPositionsBuffer,
1333 layoutBidiParameters,
1339 // Sets the positions of the glyphs.
1340 SetGlyphPositions( glyphsBuffer + index,
1341 layout.numberOfGlyphs,
1343 layoutParameters.interGlyphExtraAdvance,
1344 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
1347 // Updates the vertical pen's position.
1348 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1350 // Increase the glyph index.
1353 } // end for() traversing glyphs.
1355 if( updateCurrentBuffer )
1357 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1358 newGlyphPositions.Begin(),
1359 newGlyphPositions.End() );
1360 glyphPositions.Resize( totalNumberOfGlyphs );
1362 newLines.Resize( numberOfLines );
1364 // Current text's layout size adds only the newly laid-out lines.
1365 // Updates the layout size with the previously laid-out lines.
1366 UpdateLayoutSize( lines,
1369 if( 0u != newLines.Count() )
1371 const LineRun& lastLine = *( newLines.End() - 1u );
1373 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1374 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1376 // Update the indices of the runs before the new laid-out lines are inserted.
1377 UpdateLineIndexOffsets( layoutParameters,
1382 // Insert the lines.
1383 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1390 lines.Resize( numberOfLines );
1393 // Rounds upward to avoid a non integer size.
1394 layoutSize.height = std::ceil( layoutSize.height );
1396 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1401 void Align( const Size& size,
1402 CharacterIndex startIndex,
1403 Length numberOfCharacters,
1404 Text::HorizontalAlignment::Type horizontalAlignment,
1405 Vector<LineRun>& lines,
1406 float& alignmentOffset,
1407 Dali::LayoutDirection::Type layoutDirection,
1408 bool matchSystemLanguageDirection )
1410 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1412 alignmentOffset = MAX_FLOAT;
1413 // Traverse all lines and align the glyphs.
1414 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1418 LineRun& line = *it;
1420 if( line.characterRun.characterIndex < startIndex )
1422 // Do not align lines which have already been aligned.
1426 if( line.characterRun.characterIndex > lastCharacterPlusOne )
1428 // Do not align lines beyond the last laid-out character.
1432 if( line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast( lines, it ) )
1434 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1438 // Calculate the line's alignment offset accordingly with the align option,
1439 // the box width, line length, and the paragraph's direction.
1440 CalculateHorizontalAlignment( size.width,
1441 horizontalAlignment,
1444 matchSystemLanguageDirection );
1446 // Updates the alignment offset.
1447 alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1451 void CalculateHorizontalAlignment( float boxWidth,
1452 HorizontalAlignment::Type horizontalAlignment,
1454 Dali::LayoutDirection::Type layoutDirection,
1455 bool matchSystemLanguageDirection )
1457 line.alignmentOffset = 0.f;
1458 const bool isLineRTL = RTL == line.direction;
1460 // Whether to swap the alignment.
1461 // Swap if the line is RTL and is not required to match the direction of the system's language or if it's required to match the direction of the system's language and it's RTL.
1462 bool isLayoutRTL = isLineRTL;
1463 float lineLength = line.width;
1465 // match align for system language direction
1466 if( matchSystemLanguageDirection )
1468 // Swap the alignment type if the line is right to left.
1469 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1471 // Calculate the horizontal line offset.
1472 switch( horizontalAlignment )
1474 case HorizontalAlignment::BEGIN:
1480 lineLength += line.extraLength;
1483 line.alignmentOffset = boxWidth - lineLength;
1487 line.alignmentOffset = 0.f;
1491 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1492 line.alignmentOffset -= line.extraLength;
1497 case HorizontalAlignment::CENTER:
1499 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1503 line.alignmentOffset -= line.extraLength;
1506 line.alignmentOffset = std::floor( line.alignmentOffset ); // floor() avoids pixel alignment issues.
1509 case HorizontalAlignment::END:
1513 line.alignmentOffset = 0.f;
1517 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1518 line.alignmentOffset -= line.extraLength;
1525 lineLength += line.extraLength;
1528 line.alignmentOffset = boxWidth - lineLength;
1535 void Initialize( LineRun& line )
1537 line.glyphRun.glyphIndex = 0u;
1538 line.glyphRun.numberOfGlyphs = 0u;
1539 line.characterRun.characterIndex = 0u;
1540 line.characterRun.numberOfCharacters = 0u;
1542 line.ascender = 0.f;
1543 line.descender = 0.f;
1544 line.extraLength = 0.f;
1545 line.alignmentOffset = 0.f;
1546 line.direction = LTR;
1547 line.ellipsis = false;
1548 line.lineSpacing = mDefaultLineSpacing;
1553 float mDefaultLineSpacing;
1555 IntrusivePtr<Metrics> mMetrics;
1561 mImpl = new Engine::Impl();
1569 void Engine::SetMetrics( MetricsPtr& metrics )
1571 mImpl->mMetrics = metrics;
1574 void Engine::SetLayout( Type layout )
1576 mImpl->mLayout = layout;
1579 Engine::Type Engine::GetLayout() const
1581 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1582 return mImpl->mLayout;
1585 void Engine::SetCursorWidth( int width )
1587 mImpl->mCursorWidth = static_cast<float>( width );
1590 int Engine::GetCursorWidth() const
1592 return static_cast<int>( mImpl->mCursorWidth );
1595 bool Engine::LayoutText( Parameters& layoutParameters,
1597 bool elideTextEnabled,
1598 bool& isAutoScrollEnabled )
1600 return mImpl->LayoutText( layoutParameters,
1603 isAutoScrollEnabled );
1606 void Engine::Align( const Size& size,
1607 CharacterIndex startIndex,
1608 Length numberOfCharacters,
1609 Text::HorizontalAlignment::Type horizontalAlignment,
1610 Vector<LineRun>& lines,
1611 float& alignmentOffset,
1612 Dali::LayoutDirection::Type layoutDirection,
1613 bool matchSystemLanguageDirection )
1618 horizontalAlignment,
1622 matchSystemLanguageDirection );
1625 void Engine::SetDefaultLineSpacing( float lineSpacing )
1627 mImpl->mDefaultLineSpacing = lineSpacing;
1630 float Engine::GetDefaultLineSpacing() const
1632 return mImpl->mDefaultLineSpacing;
1635 } // namespace Layout
1639 } // namespace Toolkit