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;
56 const float MIN_LINE_SIZE = 0.f;
58 inline bool isEmptyLineAtLast( const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line )
60 return ( (*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End() );
66 * @brief Stores temporary layout info of the line.
74 numberOfCharacters{ 0u },
75 ascender{ -MAX_FLOAT },
76 descender{ MAX_FLOAT },
79 previousAdvance{ 0.f },
81 whiteSpaceLengthEndOfLine{ 0.f },
93 numberOfCharacters = 0u;
94 ascender = -MAX_FLOAT;
95 descender = MAX_FLOAT;
99 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
100 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
101 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
102 Length numberOfCharacters; ///< The number of characters which fit in one line.
103 float ascender; ///< The maximum ascender of all fonts in the line.
104 float descender; ///< The minimum descender of all fonts in the line.
105 float lineSpacing; ///< The line spacing
106 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
107 float previousAdvance; ///< The advance of the previous glyph.
108 float length; ///< The current length of the line.
109 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
110 CharacterDirection direction;
113 struct LayoutBidiParameters
117 paragraphDirection = LTR;
118 bidiParagraphIndex = 0u;
120 isBidirectional = false;
123 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
124 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
125 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
126 bool isBidirectional = false; ///< Whether the text is bidirectional.
132 : mLayout{ Layout::Engine::SINGLE_LINE_BOX },
134 mDefaultLineSpacing{ LINE_SPACING },
135 mDefaultLineSize{ MIN_LINE_SIZE }
140 * @brief Updates the line ascender and descender with the metrics of a new font.
142 * @param[in] glyphMetrics The metrics of the new font.
143 * @param[in,out] lineLayout The line layout.
145 void UpdateLineHeight( const GlyphMetrics& glyphMetrics, LineLayout& lineLayout )
147 Text::FontMetrics fontMetrics;
148 if( 0u != glyphMetrics.fontId )
150 mMetrics->GetFontMetrics( glyphMetrics.fontId, fontMetrics );
154 fontMetrics.ascender = glyphMetrics.fontHeight;
155 fontMetrics.descender = 0.f;
156 fontMetrics.height = fontMetrics.ascender;
157 fontMetrics.underlinePosition = 0.f;
158 fontMetrics.underlineThickness = 1.f;
161 // Sets the maximum ascender.
162 lineLayout.ascender = std::max( lineLayout.ascender, fontMetrics.ascender );
164 // Sets the minimum descender.
165 lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender );
167 // Sets the line size
168 lineLayout.lineSpacing = mDefaultLineSize - ( lineLayout.ascender + -lineLayout.descender );
169 lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
171 // Add the line spacing
172 lineLayout.lineSpacing += mDefaultLineSpacing;
176 * @brief Merges a temporary line layout into the line layout.
178 * @param[in,out] lineLayout The line layout.
179 * @param[in] tmpLineLayout A temporary line layout.
181 void MergeLineLayout( LineLayout& lineLayout,
182 const LineLayout& tmpLineLayout )
184 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
185 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
187 lineLayout.penX = tmpLineLayout.penX;
188 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
190 lineLayout.length = tmpLineLayout.length;
191 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
193 // Sets the maximum ascender.
194 lineLayout.ascender = std::max( lineLayout.ascender, tmpLineLayout.ascender );
196 // Sets the minimum descender.
197 lineLayout.descender = std::min( lineLayout.descender, tmpLineLayout.descender );
200 void LayoutRightToLeft( const Parameters& parameters,
201 const BidirectionalLineInfoRun& bidirectionalLineInfo,
203 float& whiteSpaceLengthEndOfLine )
205 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
206 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
207 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
208 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
210 const float outlineWidth = static_cast<float>( parameters.textModel->GetOutlineWidth() );
211 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
213 CharacterIndex characterLogicalIndex = 0u;
214 CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
216 if( RTL == bidirectionalLineInfo.direction )
218 while( TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) ) )
220 const GlyphInfo& glyphInfo = *( glyphsBuffer + *( charactersToGlyphsBuffer + characterVisualIndex ) );
222 whiteSpaceLengthEndOfLine += glyphInfo.advance;
224 ++characterLogicalIndex;
225 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
229 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
231 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
232 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
233 lastGlyphOfParagraphPlusOne,
234 charactersPerGlyphBuffer );
236 GlyphMetrics glyphMetrics;
237 GetGlyphsMetrics( glyphIndex,
238 numberOfGLyphsInGroup,
243 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
245 // Traverses the characters of the right to left paragraph.
246 for( ; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters; )
248 // Convert the character in the logical order into the character in the visual order.
249 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
250 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) );
252 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
254 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
255 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
256 lastGlyphOfParagraphPlusOne,
257 charactersPerGlyphBuffer );
259 characterLogicalIndex += *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
261 GlyphMetrics glyphMetrics;
262 GetGlyphsMetrics( glyphIndex,
263 numberOfGLyphsInGroup,
270 if( RTL == bidirectionalLineInfo.direction )
272 length += glyphMetrics.advance;
276 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
278 penX += glyphMetrics.advance;
282 if( LTR == bidirectionalLineInfo.direction )
284 whiteSpaceLengthEndOfLine = 0.f;
286 length = std::max( length, penX + glyphMetrics.xBearing + glyphMetrics.width );
287 penX += ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
292 void ReorderBiDiLayout( const Parameters& parameters,
293 LayoutBidiParameters& bidiParameters,
294 const LineLayout& currentLineLayout,
295 LineLayout& lineLayout,
296 bool breakInCharacters )
298 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
300 // The last glyph to be laid-out.
301 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
303 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
305 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
306 if( ( lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex ) &&
307 ( lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters ) )
309 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
311 // Sets the visual to logical map tables needed to reorder the text.
312 ReorderLine( bidirectionalParagraphInfo,
313 bidirectionalLinesInfo,
314 bidiParameters.bidiLineIndex,
315 lineLayout.characterIndex,
316 lineLayout.numberOfCharacters,
317 bidiParameters.paragraphDirection );
319 // Recalculate the length of the line and update the layout.
320 const BidirectionalLineInfoRun& bidirectionalLineInfo = *( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
322 if( !bidirectionalLineInfo.isIdentity )
325 float whiteSpaceLengthEndOfLine = 0.f;
326 LayoutRightToLeft( parameters,
327 bidirectionalLineInfo,
329 whiteSpaceLengthEndOfLine );
331 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
332 if( !Equals( length, lineLayout.length ) )
334 const bool isMultiline = mLayout == MULTI_LINE_BOX;
336 if( isMultiline && ( length > parameters.boundingBox.width ) )
338 if( breakInCharacters || ( isMultiline && ( 0u == currentLineLayout.numberOfGlyphs ) ) )
340 // The word doesn't fit in one line. It has to be split by character.
342 // Remove the last laid out glyph(s) as they doesn't fit.
343 for( GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex; )
345 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
346 lastGlyphOfParagraphPlusOne,
347 charactersPerGlyphBuffer );
349 const Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
351 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
352 lineLayout.numberOfCharacters -= numberOfCharacters;
354 AdjustLayout( parameters,
356 bidirectionalParagraphInfo,
359 if( lineLayout.length < parameters.boundingBox.width )
364 if( glyphIndex < numberOfGLyphsInGroup )
366 // avoids go under zero for an unsigned int.
370 glyphIndex -= numberOfGLyphsInGroup;
375 lineLayout = currentLineLayout;
377 AdjustLayout( parameters,
379 bidirectionalParagraphInfo,
385 lineLayout.length = std::max( length, lineLayout.length );
392 void AdjustLayout( const Parameters& parameters,
393 LayoutBidiParameters& bidiParameters,
394 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
395 LineLayout& lineLayout )
397 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
399 // Remove current reordered line.
400 bidirectionalLinesInfo.Erase( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
402 // Re-build the conversion table without the removed glyphs.
403 ReorderLine( bidirectionalParagraphInfo,
404 bidirectionalLinesInfo,
405 bidiParameters.bidiLineIndex,
406 lineLayout.characterIndex,
407 lineLayout.numberOfCharacters,
408 bidiParameters.paragraphDirection );
410 const BidirectionalLineInfoRun& bidirectionalLineInfo = *( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
413 float whiteSpaceLengthEndOfLine = 0.f;
414 LayoutRightToLeft( parameters,
415 bidirectionalLineInfo,
417 whiteSpaceLengthEndOfLine );
419 lineLayout.length = length;
420 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
424 * Retrieves the line layout for a given box width.
426 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
427 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
428 * to reorder the line and recalculate its length.
431 * @param[in] parameters The layout parameters.
432 * @param[] bidiParameters Bidirectional info for the current line.
433 * @param[out] lineLayout The line layout.
434 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
436 void GetLineLayoutForBox( const Parameters& parameters,
437 LayoutBidiParameters& bidiParameters,
438 LineLayout& lineLayout,
439 bool completelyFill )
441 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
442 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
444 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
445 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
446 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
447 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
448 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
450 const float outlineWidth = static_cast<float>( parameters.textModel->GetOutlineWidth() );
451 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
453 const bool isMultiline = mLayout == MULTI_LINE_BOX;
454 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD;
456 // The last glyph to be laid-out.
457 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
459 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
460 // In the case the line starts with a right to left character, if the width is longer than the advance,
461 // the difference needs to be added to the line length.
463 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
464 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
465 lastGlyphOfParagraphPlusOne,
466 charactersPerGlyphBuffer );
468 GlyphMetrics glyphMetrics;
469 GetGlyphsMetrics( lineLayout.glyphIndex,
470 numberOfGLyphsInGroup,
475 // Set the direction of the first character of the line.
476 lineLayout.characterIndex = *( glyphsToCharactersBuffer + lineLayout.glyphIndex );
478 // Stores temporary line layout which has not been added to the final line layout.
479 LineLayout tmpLineLayout;
481 // Initialize the start point.
483 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
484 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
485 // 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.
486 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
488 // Calculate the line height if there is no characters.
489 FontId lastFontId = glyphMetrics.fontId;
490 UpdateLineHeight( glyphMetrics, tmpLineLayout );
492 bool oneWordLaidOut = false;
494 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
495 glyphIndex < lastGlyphOfParagraphPlusOne; )
497 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
499 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
500 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
501 lastGlyphOfParagraphPlusOne,
502 charactersPerGlyphBuffer );
504 GlyphMetrics glyphMetrics;
505 GetGlyphsMetrics( glyphIndex,
506 numberOfGLyphsInGroup,
511 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
513 // Check if the font of the current glyph is the same of the previous one.
514 // If it's different the ascender and descender need to be updated.
515 if( lastFontId != glyphMetrics.fontId )
517 UpdateLineHeight( glyphMetrics, tmpLineLayout );
518 lastFontId = glyphMetrics.fontId;
521 // Get the character indices for the current glyph. The last character index is needed
522 // because there are glyphs formed by more than one character but their break info is
523 // given only for the last character.
524 const Length charactersPerGlyph = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
525 const bool hasCharacters = charactersPerGlyph > 0u;
526 const CharacterIndex characterFirstIndex = *( glyphsToCharactersBuffer + glyphIndex );
527 const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
529 // Get the line break info for the current character.
530 const LineBreakInfo lineBreakInfo = hasCharacters ? *( lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
532 // Increase the number of characters.
533 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
535 // Increase the number of glyphs.
536 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
538 // Check whether is a white space.
539 const Character character = *( textBuffer + characterFirstIndex );
540 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
542 // Calculate the length of the line.
544 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
545 const float previousTmpPenX = tmpLineLayout.penX;
546 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
547 const float previousTmpLength = tmpLineLayout.length;
548 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
552 // Add the length to the length of white spaces at the end of the line.
553 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
557 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
558 tmpLineLayout.previousAdvance = ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
560 tmpLineLayout.length = std::max( tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width );
562 // Clear the white space length at the end of the line.
563 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
566 // Check if the accumulated length fits in the width of the box.
567 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
568 ( tmpLineLayout.length > parameters.boundingBox.width ) )
570 // Current word does not fit in the box's width.
571 if( !oneWordLaidOut || completelyFill )
573 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
575 // The word doesn't fit in the control's width. It needs to be split by character.
576 if( tmpLineLayout.numberOfGlyphs > 0u )
578 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
579 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
581 tmpLineLayout.penX = previousTmpPenX;
582 tmpLineLayout.previousAdvance = previousTmpAdvance;
583 tmpLineLayout.length = previousTmpLength;
584 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
587 // Add part of the word to the line layout.
588 MergeLineLayout( lineLayout, tmpLineLayout );
592 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
595 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
597 // Reorder the RTL line.
598 if( bidiParameters.isBidirectional )
600 ReorderBiDiLayout( parameters,
610 if( ( isMultiline || isLastGlyph ) &&
611 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
613 LineLayout currentLineLayout = lineLayout;
615 // Must break the line. Update the line layout and return.
616 MergeLineLayout( lineLayout, tmpLineLayout );
618 // Reorder the RTL line.
619 if( bidiParameters.isBidirectional )
621 ReorderBiDiLayout( parameters,
628 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
629 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
635 ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
637 oneWordLaidOut = isWordLaidOut;
638 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
640 // Current glyph is the last one of the current word.
641 // Add the temporal layout to the current one.
642 MergeLineLayout( lineLayout, tmpLineLayout );
644 tmpLineLayout.Clear();
647 glyphIndex += numberOfGLyphsInGroup;
650 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
653 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
654 Length numberOfGlyphs,
656 float interGlyphExtraAdvance,
657 Vector2* glyphPositionsBuffer )
659 // Traverse the glyphs and set the positions.
661 // Check if the x bearing of the first character is negative.
662 // If it has a negative x bearing, it will exceed the boundaries of the actor,
663 // so the penX position needs to be moved to the right.
665 const GlyphInfo& glyph = *glyphsBuffer;
666 float penX = -glyph.xBearing + mCursorWidth + outlineWidth;
668 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
670 const GlyphInfo& glyph = *( glyphsBuffer + i );
671 Vector2& position = *( glyphPositionsBuffer + i );
673 position.x = penX + glyph.xBearing;
674 position.y = -glyph.yBearing;
676 penX += ( glyph.advance + interGlyphExtraAdvance );
680 void SetGlyphPositions( const Parameters& layoutParameters,
681 Vector2* glyphPositionsBuffer,
682 LayoutBidiParameters& layoutBidiParameters,
683 const LineLayout& layout )
685 const Character* const textBuffer = layoutParameters.textModel->mLogicalModel->mText.Begin();
686 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
687 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
688 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
689 const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
691 CharacterIndex characterLogicalIndex = 0u;
692 CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
695 while( TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) ) )
697 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
698 const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
700 Vector2& position = *( glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex );
702 position.y = -glyph.yBearing;
704 penX += glyph.advance;
706 ++characterLogicalIndex;
707 characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
710 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
711 const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
713 penX += -glyph.xBearing;
715 // Traverses the characters of the right to left paragraph.
716 for( ; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
717 ++characterLogicalIndex )
719 // Convert the character in the logical order into the character in the visual order.
720 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
722 // Get the number of glyphs of the character.
723 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterVisualIndex );
725 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
727 // Convert the character in the visual order into the glyph in the visual order.
728 const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex ) + index;
730 DALI_ASSERT_DEBUG( glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count() );
732 const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
733 Vector2& position = *( glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex );
735 position.x = penX + glyph.xBearing;
736 position.y = -glyph.yBearing;
738 penX += ( glyph.advance + layoutParameters.interGlyphExtraAdvance );
744 * @brief Resizes the line buffer.
746 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
747 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
748 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
749 * @param[in] updateCurrentBuffer Whether the layout is updated.
751 * @return Pointer to either lines or newLines.
753 LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
754 Vector<LineRun>& newLines,
755 Length& linesCapacity,
756 bool updateCurrentBuffer )
758 LineRun* linesBuffer = nullptr;
759 // Reserve more space for the next lines.
761 if( updateCurrentBuffer )
763 newLines.Resize( linesCapacity );
764 linesBuffer = newLines.Begin();
768 lines.Resize( linesCapacity );
769 linesBuffer = lines.Begin();
776 * Ellipsis a line if it exceeds the width's of the bounding box.
778 * @param[in] layoutParameters The parameters needed to layout the text.
779 * @param[in] layout The line layout.
780 * @param[in,out] layoutSize The text's layout size.
781 * @param[in,out] linesBuffer Pointer to the line's buffer.
782 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
783 * @param[in,out] numberOfLines The number of laid-out lines.
784 * @param[in] penY The vertical layout position.
785 * @param[in] currentParagraphDirection The current paragraph's direction.
786 * @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
788 * return Whether the line is ellipsized.
790 bool EllipsisLine( const Parameters& layoutParameters,
791 LayoutBidiParameters& layoutBidiParameters,
792 const LineLayout& layout,
794 LineRun* linesBuffer,
795 Vector2* glyphPositionsBuffer,
796 Length& numberOfLines,
798 bool& isAutoScrollEnabled )
800 const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) :
801 ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
802 ( ( mLayout == SINGLE_LINE_BOX ) &&
803 ( layout.length > layoutParameters.boundingBox.width ) ) );
807 isAutoScrollEnabled = false;
808 // Do not layout more lines if ellipsis is enabled.
810 // The last line needs to be completely filled with characters.
811 // Part of a word may be used.
813 LineRun* lineRun = nullptr;
814 LineLayout ellipsisLayout;
815 if( 0u != numberOfLines )
817 // Get the last line and layout it again with the 'completelyFill' flag to true.
818 lineRun = linesBuffer + ( numberOfLines - 1u );
820 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
822 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
826 // At least there is space reserved for one line.
827 lineRun = linesBuffer;
829 lineRun->glyphRun.glyphIndex = 0u;
830 ellipsisLayout.glyphIndex = 0u;
835 GetLineLayoutForBox( layoutParameters,
836 layoutBidiParameters,
840 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
841 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
842 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
843 lineRun->width = ellipsisLayout.length;
844 lineRun->extraLength = std::ceil( ellipsisLayout.whiteSpaceLengthEndOfLine );
845 lineRun->ascender = ellipsisLayout.ascender;
846 lineRun->descender = ellipsisLayout.descender;
847 lineRun->ellipsis = true;
849 layoutSize.width = layoutParameters.boundingBox.width;
850 if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
852 layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
855 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
856 const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
858 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
860 if( layoutBidiParameters.isBidirectional )
862 layoutBidiParameters.bidiLineIndex = 0u;
863 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
864 endIt = bidirectionalLinesInfo.End();
866 ++it, ++layoutBidiParameters.bidiLineIndex )
868 const BidirectionalLineInfoRun& run = *it;
870 if( ellipsisLayout.characterIndex == run.characterRun.characterIndex )
872 // Found where to insert the bidi line info.
878 const BidirectionalLineInfoRun* const bidirectionalLineInfo = ( layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty() ) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
880 if( ( nullptr != bidirectionalLineInfo ) &&
881 !bidirectionalLineInfo->isIdentity &&
882 ( ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex ) )
884 lineRun->direction = RTL;
885 SetGlyphPositions( layoutParameters,
886 glyphPositionsBuffer,
887 layoutBidiParameters,
892 lineRun->direction = LTR;
893 SetGlyphPositions( glyphsBuffer + lineRun->glyphRun.glyphIndex,
894 ellipsisLayout.numberOfGlyphs,
896 layoutParameters.interGlyphExtraAdvance,
897 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
905 * @brief Updates the text layout with a new laid-out line.
907 * @param[in] layoutParameters The parameters needed to layout the text.
908 * @param[in] layout The line layout.
909 * @param[in,out] layoutSize The text's layout size.
910 * @param[in,out] linesBuffer Pointer to the line's buffer.
911 * @param[in] index Index to the vector of glyphs.
912 * @param[in,out] numberOfLines The number of laid-out lines.
913 * @param[in] isLastLine Whether the laid-out line is the last one.
915 void UpdateTextLayout( const Parameters& layoutParameters,
916 const LineLayout& layout,
918 LineRun* linesBuffer,
920 Length& numberOfLines,
923 LineRun& lineRun = *( linesBuffer + numberOfLines );
926 lineRun.glyphRun.glyphIndex = index;
927 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
928 lineRun.characterRun.characterIndex = layout.characterIndex;
929 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
930 lineRun.width = layout.length;
931 lineRun.extraLength = std::ceil( layout.whiteSpaceLengthEndOfLine );
934 // Rounds upward to avoid a non integer size.
935 lineRun.width = std::ceil( lineRun.width );
937 lineRun.ascender = layout.ascender;
938 lineRun.descender = layout.descender;
939 lineRun.direction = layout.direction;
940 lineRun.ellipsis = false;
942 lineRun.lineSpacing = mDefaultLineSize - ( lineRun.ascender + -lineRun.descender );
943 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
945 lineRun.lineSpacing += mDefaultLineSpacing;
948 // Update the actual size.
949 if( lineRun.width > layoutSize.width )
951 layoutSize.width = lineRun.width;
954 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
958 * @brief Updates the text layout with the last laid-out line.
960 * @param[in] layoutParameters The parameters needed to layout the text.
961 * @param[in] characterIndex The character index of the line.
962 * @param[in] glyphIndex The glyph index of the line.
963 * @param[in,out] layoutSize The text's layout size.
964 * @param[in,out] linesBuffer Pointer to the line's buffer.
965 * @param[in,out] numberOfLines The number of laid-out lines.
967 void UpdateTextLayout( const Parameters& layoutParameters,
968 CharacterIndex characterIndex,
969 GlyphIndex glyphIndex,
971 LineRun* linesBuffer,
972 Length& numberOfLines )
974 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
976 // Need to add a new line with no characters but with height to increase the layoutSize.height
977 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
979 Text::FontMetrics fontMetrics;
980 if( 0u != glyphInfo.fontId )
982 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
985 LineRun& lineRun = *( linesBuffer + numberOfLines );
988 lineRun.glyphRun.glyphIndex = glyphIndex;
989 lineRun.glyphRun.numberOfGlyphs = 0u;
990 lineRun.characterRun.characterIndex = characterIndex;
991 lineRun.characterRun.numberOfCharacters = 0u;
993 lineRun.ascender = fontMetrics.ascender;
994 lineRun.descender = fontMetrics.descender;
995 lineRun.extraLength = 0.f;
996 lineRun.alignmentOffset = 0.f;
997 lineRun.direction = LTR;
998 lineRun.ellipsis = false;
1000 lineRun.lineSpacing = mDefaultLineSize - ( lineRun.ascender + -lineRun.descender );
1001 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1003 lineRun.lineSpacing += mDefaultLineSpacing;
1005 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
1009 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1011 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1012 * @param[in,out] layoutSize The text's layout size.
1014 void UpdateLayoutSize( const Vector<LineRun>& lines,
1017 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
1018 endIt = lines.End();
1022 const LineRun& line = *it;
1024 if( line.width > layoutSize.width )
1026 layoutSize.width = line.width;
1029 layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
1034 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1036 * @param[in] layoutParameters The parameters needed to layout the text.
1037 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1038 * @param[in] characterOffset The offset to be added to the runs of characters.
1039 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1041 void UpdateLineIndexOffsets( const Parameters& layoutParameters,
1042 Vector<LineRun>& lines,
1043 Length characterOffset,
1044 Length glyphOffset )
1046 // Update the glyph and character runs.
1047 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1048 endIt = lines.End();
1052 LineRun& line = *it;
1054 line.glyphRun.glyphIndex = glyphOffset;
1055 line.characterRun.characterIndex = characterOffset;
1057 glyphOffset += line.glyphRun.numberOfGlyphs;
1058 characterOffset += line.characterRun.numberOfCharacters;
1062 bool LayoutText( Parameters& layoutParameters,
1064 bool elideTextEnabled,
1065 bool& isAutoScrollEnabled )
1067 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
1068 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
1070 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1072 if( 0u == layoutParameters.numberOfGlyphs )
1074 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1075 if( layoutParameters.isLastNewParagraph )
1077 Length numberOfLines = lines.Count();
1078 if( 0u != numberOfLines )
1080 const LineRun& lastLine = *( lines.End() - 1u );
1082 if( 0u != lastLine.characterRun.numberOfCharacters )
1084 // Need to add a new line with no characters but with height to increase the layoutSize.height
1086 Initialize( newLine );
1087 lines.PushBack( newLine );
1089 UpdateTextLayout( layoutParameters,
1090 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1091 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1099 // Calculates the layout size.
1100 UpdateLayoutSize( lines,
1103 // Rounds upward to avoid a non integer size.
1104 layoutSize.height = std::ceil( layoutSize.height );
1106 // Nothing else do if there are no glyphs to layout.
1110 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1111 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1112 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1114 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1115 // This extra line needs to be removed.
1116 if( 0u != lines.Count() )
1118 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1120 if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
1121 ( lastGlyphPlusOne == totalNumberOfGlyphs ) )
1123 lines.Remove( lastLine );
1127 // Retrieve BiDi info.
1128 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1130 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1131 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1132 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1134 // Set the layout bidirectional paramters.
1135 LayoutBidiParameters layoutBidiParameters;
1137 // Whether the layout is being updated or set from scratch.
1138 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1140 Vector2* glyphPositionsBuffer = nullptr;
1141 Vector<Vector2> newGlyphPositions;
1143 LineRun* linesBuffer = nullptr;
1144 Vector<LineRun> newLines;
1146 // Estimate the number of lines.
1147 Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
1148 Length numberOfLines = 0u;
1150 if( updateCurrentBuffer )
1152 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
1153 glyphPositionsBuffer = newGlyphPositions.Begin();
1155 newLines.Resize( linesCapacity );
1156 linesBuffer = newLines.Begin();
1160 glyphPositionsBuffer = glyphPositions.Begin();
1162 lines.Resize( linesCapacity );
1163 linesBuffer = lines.Begin();
1166 float penY = CalculateLineOffset( lines,
1167 layoutParameters.startLineIndex );
1168 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
1170 layoutBidiParameters.Clear();
1172 if( hasBidiParagraphs )
1174 const CharacterIndex startCharacterIndex = *( glyphsToCharactersBuffer + index );
1176 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1177 endIt = bidirectionalParagraphsInfo.End();
1179 ++it, ++layoutBidiParameters.bidiParagraphIndex )
1181 const BidirectionalParagraphInfoRun& run = *it;
1183 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1185 if( lastCharacterIndex <= startCharacterIndex )
1187 // Do not process, the paragraph has already been processed.
1191 if( startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex )
1193 layoutBidiParameters.paragraphDirection = run.direction;
1194 layoutBidiParameters.isBidirectional = true;
1197 // Has already been found.
1201 if( layoutBidiParameters.isBidirectional )
1203 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1204 endIt = bidirectionalLinesInfo.End();
1206 ++it, ++layoutBidiParameters.bidiLineIndex )
1208 const BidirectionalLineInfoRun& run = *it;
1210 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1212 if( lastCharacterIndex <= startCharacterIndex )
1218 if( startCharacterIndex < lastCharacterIndex )
1220 // Found where to insert the bidi line info.
1227 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1229 // Get the layout for the line.
1231 layout.direction = layoutBidiParameters.paragraphDirection;
1232 layout.glyphIndex = index;
1233 GetLineLayoutForBox( layoutParameters,
1234 layoutBidiParameters,
1238 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
1239 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
1240 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
1241 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
1242 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
1244 if( 0u == layout.numberOfGlyphs )
1246 // The width is too small and no characters are laid-out.
1247 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
1249 lines.Resize( numberOfLines );
1251 // Rounds upward to avoid a non integer size.
1252 layoutSize.height = std::ceil( layoutSize.height );
1257 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
1259 penY += layout.ascender;
1261 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
1263 bool ellipsis = false;
1264 if( elideTextEnabled )
1266 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1268 // Does the ellipsis of the last line.
1269 ellipsis = EllipsisLine( layoutParameters,
1270 layoutBidiParameters,
1274 glyphPositionsBuffer,
1277 isAutoScrollEnabled );
1282 // No more lines to layout.
1287 // Whether the last line has been laid-out.
1288 const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
1290 if( numberOfLines == linesCapacity )
1293 // Reserve more space for the next lines.
1294 linesBuffer = ResizeLinesBuffer( lines,
1297 updateCurrentBuffer );
1300 // Updates the current text's layout with the line's layout.
1301 UpdateTextLayout( layoutParameters,
1309 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
1311 if( ( nextIndex == totalNumberOfGlyphs ) &&
1312 layoutParameters.isLastNewParagraph &&
1313 ( mLayout == MULTI_LINE_BOX ) )
1315 // The last character of the text is a new paragraph character.
1316 // An extra line with no characters is added to increase the text's height
1317 // in order to place the cursor.
1319 if( numberOfLines == linesCapacity )
1321 // Reserve more space for the next lines.
1322 linesBuffer = ResizeLinesBuffer( lines,
1325 updateCurrentBuffer );
1328 UpdateTextLayout( layoutParameters,
1329 layout.characterIndex + layout.numberOfCharacters,
1330 index + layout.numberOfGlyphs,
1334 } // whether to add a last line.
1336 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1337 const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
1339 const BidirectionalLineInfoRun* const bidirectionalLineInfo = ( layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty() ) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1341 if( ( nullptr != bidirectionalLineInfo ) &&
1342 !bidirectionalLineInfo->isIdentity &&
1343 ( layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex ) )
1345 SetGlyphPositions( layoutParameters,
1346 glyphPositionsBuffer,
1347 layoutBidiParameters,
1353 // Sets the positions of the glyphs.
1354 SetGlyphPositions( glyphsBuffer + index,
1355 layout.numberOfGlyphs,
1357 layoutParameters.interGlyphExtraAdvance,
1358 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
1361 // Updates the vertical pen's position.
1362 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1363 // If there is a defaultLineSize, updates the pen's position.
1364 if( mDefaultLineSize > 0.f )
1366 float lineSpacing = mDefaultLineSize - ( layout.ascender + -layout.descender );
1367 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
1368 penY += lineSpacing;
1371 // Increase the glyph index.
1374 } // end for() traversing glyphs.
1376 if( updateCurrentBuffer )
1378 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1379 newGlyphPositions.Begin(),
1380 newGlyphPositions.End() );
1381 glyphPositions.Resize( totalNumberOfGlyphs );
1383 newLines.Resize( numberOfLines );
1385 // Current text's layout size adds only the newly laid-out lines.
1386 // Updates the layout size with the previously laid-out lines.
1387 UpdateLayoutSize( lines,
1390 if( 0u != newLines.Count() )
1392 const LineRun& lastLine = *( newLines.End() - 1u );
1394 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1395 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1397 // Update the indices of the runs before the new laid-out lines are inserted.
1398 UpdateLineIndexOffsets( layoutParameters,
1403 // Insert the lines.
1404 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1411 lines.Resize( numberOfLines );
1414 // Rounds upward to avoid a non integer size.
1415 layoutSize.height = std::ceil( layoutSize.height );
1417 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1422 void Align( const Size& size,
1423 CharacterIndex startIndex,
1424 Length numberOfCharacters,
1425 Text::HorizontalAlignment::Type horizontalAlignment,
1426 Vector<LineRun>& lines,
1427 float& alignmentOffset,
1428 Dali::LayoutDirection::Type layoutDirection,
1429 bool matchSystemLanguageDirection )
1431 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1433 alignmentOffset = MAX_FLOAT;
1434 // Traverse all lines and align the glyphs.
1435 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1439 LineRun& line = *it;
1441 if( line.characterRun.characterIndex < startIndex )
1443 // Do not align lines which have already been aligned.
1447 if( line.characterRun.characterIndex > lastCharacterPlusOne )
1449 // Do not align lines beyond the last laid-out character.
1453 if( line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast( lines, it ) )
1455 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1459 // Calculate the line's alignment offset accordingly with the align option,
1460 // the box width, line length, and the paragraph's direction.
1461 CalculateHorizontalAlignment( size.width,
1462 horizontalAlignment,
1465 matchSystemLanguageDirection );
1467 // Updates the alignment offset.
1468 alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1472 void CalculateHorizontalAlignment( float boxWidth,
1473 HorizontalAlignment::Type horizontalAlignment,
1475 Dali::LayoutDirection::Type layoutDirection,
1476 bool matchSystemLanguageDirection )
1478 line.alignmentOffset = 0.f;
1479 const bool isLineRTL = RTL == line.direction;
1481 // Whether to swap the alignment.
1482 // 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.
1483 bool isLayoutRTL = isLineRTL;
1484 float lineLength = line.width;
1486 // match align for system language direction
1487 if( matchSystemLanguageDirection )
1489 // Swap the alignment type if the line is right to left.
1490 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1492 // Calculate the horizontal line offset.
1493 switch( horizontalAlignment )
1495 case HorizontalAlignment::BEGIN:
1501 lineLength += line.extraLength;
1504 line.alignmentOffset = boxWidth - lineLength;
1508 line.alignmentOffset = 0.f;
1512 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1513 line.alignmentOffset -= line.extraLength;
1518 case HorizontalAlignment::CENTER:
1520 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1524 line.alignmentOffset -= line.extraLength;
1527 line.alignmentOffset = std::floor( line.alignmentOffset ); // floor() avoids pixel alignment issues.
1530 case HorizontalAlignment::END:
1534 line.alignmentOffset = 0.f;
1538 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1539 line.alignmentOffset -= line.extraLength;
1546 lineLength += line.extraLength;
1549 line.alignmentOffset = boxWidth - lineLength;
1556 void Initialize( LineRun& line )
1558 line.glyphRun.glyphIndex = 0u;
1559 line.glyphRun.numberOfGlyphs = 0u;
1560 line.characterRun.characterIndex = 0u;
1561 line.characterRun.numberOfCharacters = 0u;
1563 line.ascender = 0.f;
1564 line.descender = 0.f;
1565 line.extraLength = 0.f;
1566 line.alignmentOffset = 0.f;
1567 line.direction = LTR;
1568 line.ellipsis = false;
1569 line.lineSpacing = mDefaultLineSpacing;
1574 float mDefaultLineSpacing;
1575 float mDefaultLineSize;
1577 IntrusivePtr<Metrics> mMetrics;
1583 mImpl = new Engine::Impl();
1591 void Engine::SetMetrics( MetricsPtr& metrics )
1593 mImpl->mMetrics = metrics;
1596 void Engine::SetLayout( Type layout )
1598 mImpl->mLayout = layout;
1601 Engine::Type Engine::GetLayout() const
1603 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1604 return mImpl->mLayout;
1607 void Engine::SetCursorWidth( int width )
1609 mImpl->mCursorWidth = static_cast<float>( width );
1612 int Engine::GetCursorWidth() const
1614 return static_cast<int>( mImpl->mCursorWidth );
1617 bool Engine::LayoutText( Parameters& layoutParameters,
1619 bool elideTextEnabled,
1620 bool& isAutoScrollEnabled )
1622 return mImpl->LayoutText( layoutParameters,
1625 isAutoScrollEnabled );
1628 void Engine::Align( const Size& size,
1629 CharacterIndex startIndex,
1630 Length numberOfCharacters,
1631 Text::HorizontalAlignment::Type horizontalAlignment,
1632 Vector<LineRun>& lines,
1633 float& alignmentOffset,
1634 Dali::LayoutDirection::Type layoutDirection,
1635 bool matchSystemLanguageDirection )
1640 horizontalAlignment,
1644 matchSystemLanguageDirection );
1647 void Engine::SetDefaultLineSpacing( float lineSpacing )
1649 mImpl->mDefaultLineSpacing = lineSpacing;
1652 float Engine::GetDefaultLineSpacing() const
1654 return mImpl->mDefaultLineSpacing;
1657 void Engine::SetDefaultLineSize( float lineSize )
1659 mImpl->mDefaultLineSize = lineSize;
1662 float Engine::GetDefaultLineSize() const
1664 return mImpl->mDefaultLineSize;
1667 } // namespace Layout
1671 } // namespace Toolkit