2 * Copyright (c) 2017 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 = 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 = 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( 0u <= glyphIndex && 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 if( isLastLine && !layoutParameters.isLastNewParagraph )
928 lineRun.width = layout.length;
929 if( LTR == layout.direction )
931 lineRun.width += layout.whiteSpaceLengthEndOfLine;
932 lineRun.extraLength = 0.f;
936 lineRun.extraLength = layout.whiteSpaceLengthEndOfLine;
941 lineRun.width = layout.length;
942 lineRun.extraLength = std::ceil( layout.whiteSpaceLengthEndOfLine );
945 // Rounds upward to avoid a non integer size.
946 lineRun.width = std::ceil( lineRun.width );
948 lineRun.ascender = layout.ascender;
949 lineRun.descender = layout.descender;
950 lineRun.direction = layout.direction;
951 lineRun.ellipsis = false;
953 // Update the actual size.
954 if( lineRun.width > layoutSize.width )
956 layoutSize.width = lineRun.width;
959 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
963 * @brief Updates the text layout with the last laid-out line.
965 * @param[in] layoutParameters The parameters needed to layout the text.
966 * @param[in] characterIndex The character index of the line.
967 * @param[in] glyphIndex The glyph index of the line.
968 * @param[in,out] layoutSize The text's layout size.
969 * @param[in,out] linesBuffer Pointer to the line's buffer.
970 * @param[in,out] numberOfLines The number of laid-out lines.
972 void UpdateTextLayout( const Parameters& layoutParameters,
973 CharacterIndex characterIndex,
974 GlyphIndex glyphIndex,
976 LineRun* linesBuffer,
977 Length& numberOfLines )
979 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
981 // Need to add a new line with no characters but with height to increase the layoutSize.height
982 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
984 Text::FontMetrics fontMetrics;
985 if( 0u != glyphInfo.fontId )
987 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
990 LineRun& lineRun = *( linesBuffer + numberOfLines );
993 lineRun.glyphRun.glyphIndex = glyphIndex;
994 lineRun.glyphRun.numberOfGlyphs = 0u;
995 lineRun.characterRun.characterIndex = characterIndex;
996 lineRun.characterRun.numberOfCharacters = 0u;
998 lineRun.ascender = fontMetrics.ascender;
999 lineRun.descender = fontMetrics.descender;
1000 lineRun.extraLength = 0.f;
1001 lineRun.alignmentOffset = 0.f;
1002 lineRun.direction = LTR;
1003 lineRun.ellipsis = false;
1004 lineRun.lineSpacing = mDefaultLineSpacing;
1006 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
1010 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1012 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1013 * @param[in,out] layoutSize The text's layout size.
1015 void UpdateLayoutSize( const Vector<LineRun>& lines,
1018 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
1019 endIt = lines.End();
1023 const LineRun& line = *it;
1025 if( line.width > layoutSize.width )
1027 layoutSize.width = line.width;
1030 layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
1035 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1037 * @param[in] layoutParameters The parameters needed to layout the text.
1038 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1039 * @param[in] characterOffset The offset to be added to the runs of characters.
1040 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1042 void UpdateLineIndexOffsets( const Parameters& layoutParameters,
1043 Vector<LineRun>& lines,
1044 Length characterOffset,
1045 Length glyphOffset )
1047 // Update the glyph and character runs.
1048 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1049 endIt = lines.End();
1053 LineRun& line = *it;
1055 line.glyphRun.glyphIndex = glyphOffset;
1056 line.characterRun.characterIndex = characterOffset;
1058 glyphOffset += line.glyphRun.numberOfGlyphs;
1059 characterOffset += line.characterRun.numberOfCharacters;
1063 bool LayoutText( Parameters& layoutParameters,
1065 bool elideTextEnabled,
1066 bool& isAutoScrollEnabled )
1068 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
1069 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
1071 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1073 if( 0u == layoutParameters.numberOfGlyphs )
1075 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1076 if( layoutParameters.isLastNewParagraph )
1078 Length numberOfLines = lines.Count();
1079 if( 0u != numberOfLines )
1081 const LineRun& lastLine = *( lines.End() - 1u );
1083 if( 0u != lastLine.characterRun.numberOfCharacters )
1085 // Need to add a new line with no characters but with height to increase the layoutSize.height
1087 Initialize( newLine );
1088 lines.PushBack( newLine );
1090 UpdateTextLayout( layoutParameters,
1091 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1092 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1100 // Calculates the layout size.
1101 UpdateLayoutSize( lines,
1104 // Rounds upward to avoid a non integer size.
1105 layoutSize.height = std::ceil( layoutSize.height );
1107 // Nothing else do if there are no glyphs to layout.
1111 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1112 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1113 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1115 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1116 // This extra line needs to be removed.
1117 if( 0u != lines.Count() )
1119 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1121 if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
1122 ( lastGlyphPlusOne == totalNumberOfGlyphs ) )
1124 lines.Remove( lastLine );
1128 // Retrieve BiDi info.
1129 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1131 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1132 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1133 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1135 // Set the layout bidirectional paramters.
1136 LayoutBidiParameters layoutBidiParameters;
1138 // Whether the layout is being updated or set from scratch.
1139 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1141 Vector2* glyphPositionsBuffer = nullptr;
1142 Vector<Vector2> newGlyphPositions;
1144 LineRun* linesBuffer = nullptr;
1145 Vector<LineRun> newLines;
1147 // Estimate the number of lines.
1148 Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
1149 Length numberOfLines = 0u;
1151 if( updateCurrentBuffer )
1153 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
1154 glyphPositionsBuffer = newGlyphPositions.Begin();
1156 newLines.Resize( linesCapacity );
1157 linesBuffer = newLines.Begin();
1161 glyphPositionsBuffer = glyphPositions.Begin();
1163 lines.Resize( linesCapacity );
1164 linesBuffer = lines.Begin();
1167 float penY = CalculateLineOffset( lines,
1168 layoutParameters.startLineIndex );
1169 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
1171 layoutBidiParameters.Clear();
1173 if( hasBidiParagraphs )
1175 const CharacterIndex startCharacterIndex = *( glyphsToCharactersBuffer + index );
1177 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1178 endIt = bidirectionalParagraphsInfo.End();
1180 ++it, ++layoutBidiParameters.bidiParagraphIndex )
1182 const BidirectionalParagraphInfoRun& run = *it;
1184 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1186 if( lastCharacterIndex <= startCharacterIndex )
1188 // Do not process, the paragraph has already been processed.
1192 if( startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex )
1194 layoutBidiParameters.paragraphDirection = run.direction;
1195 layoutBidiParameters.isBidirectional = true;
1198 // Has already been found.
1202 if( layoutBidiParameters.isBidirectional )
1204 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1205 endIt = bidirectionalLinesInfo.End();
1207 ++it, ++layoutBidiParameters.bidiLineIndex )
1209 const BidirectionalLineInfoRun& run = *it;
1211 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1213 if( lastCharacterIndex <= startCharacterIndex )
1219 if( startCharacterIndex < lastCharacterIndex )
1221 // Found where to insert the bidi line info.
1228 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1230 // Get the layout for the line.
1232 layout.direction = layoutBidiParameters.paragraphDirection;
1233 layout.glyphIndex = index;
1234 GetLineLayoutForBox( layoutParameters,
1235 layoutBidiParameters,
1239 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
1240 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
1241 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
1242 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
1243 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
1245 if( 0u == layout.numberOfGlyphs )
1247 // The width is too small and no characters are laid-out.
1248 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
1250 lines.Resize( numberOfLines );
1252 // Rounds upward to avoid a non integer size.
1253 layoutSize.height = std::ceil( layoutSize.height );
1258 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
1260 penY += layout.ascender;
1262 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
1264 bool ellipsis = false;
1265 if( elideTextEnabled )
1267 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1269 // Does the ellipsis of the last line.
1270 ellipsis = EllipsisLine( layoutParameters,
1271 layoutBidiParameters,
1275 glyphPositionsBuffer,
1278 isAutoScrollEnabled );
1283 // No more lines to layout.
1288 // Whether the last line has been laid-out.
1289 const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
1291 if( numberOfLines == linesCapacity )
1294 // Reserve more space for the next lines.
1295 linesBuffer = ResizeLinesBuffer( lines,
1298 updateCurrentBuffer );
1301 // Updates the current text's layout with the line's layout.
1302 UpdateTextLayout( layoutParameters,
1310 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
1312 if( ( nextIndex == totalNumberOfGlyphs ) &&
1313 layoutParameters.isLastNewParagraph &&
1314 ( mLayout == MULTI_LINE_BOX ) )
1316 // The last character of the text is a new paragraph character.
1317 // An extra line with no characters is added to increase the text's height
1318 // in order to place the cursor.
1320 if( numberOfLines == linesCapacity )
1322 // Reserve more space for the next lines.
1323 linesBuffer = ResizeLinesBuffer( lines,
1326 updateCurrentBuffer );
1329 UpdateTextLayout( layoutParameters,
1330 layout.characterIndex + layout.numberOfCharacters,
1331 index + layout.numberOfGlyphs,
1335 } // whether to add a last line.
1337 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1338 const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
1340 const BidirectionalLineInfoRun* const bidirectionalLineInfo = ( layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty() ) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1342 if( ( nullptr != bidirectionalLineInfo ) &&
1343 !bidirectionalLineInfo->isIdentity &&
1344 ( layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex ) )
1346 SetGlyphPositions( layoutParameters,
1347 glyphPositionsBuffer,
1348 layoutBidiParameters,
1354 // Sets the positions of the glyphs.
1355 SetGlyphPositions( glyphsBuffer + index,
1356 layout.numberOfGlyphs,
1358 layoutParameters.interGlyphExtraAdvance,
1359 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
1362 // Updates the vertical pen's position.
1363 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1365 // Increase the glyph index.
1368 } // end for() traversing glyphs.
1370 if( updateCurrentBuffer )
1372 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1373 newGlyphPositions.Begin(),
1374 newGlyphPositions.End() );
1375 glyphPositions.Resize( totalNumberOfGlyphs );
1377 newLines.Resize( numberOfLines );
1379 // Current text's layout size adds only the newly laid-out lines.
1380 // Updates the layout size with the previously laid-out lines.
1381 UpdateLayoutSize( lines,
1384 if( 0u != newLines.Count() )
1386 const LineRun& lastLine = *( newLines.End() - 1u );
1388 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1389 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1391 // Update the indices of the runs before the new laid-out lines are inserted.
1392 UpdateLineIndexOffsets( layoutParameters,
1397 // Insert the lines.
1398 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1405 lines.Resize( numberOfLines );
1408 // Rounds upward to avoid a non integer size.
1409 layoutSize.height = std::ceil( layoutSize.height );
1411 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1416 void Align( const Size& size,
1417 CharacterIndex startIndex,
1418 Length numberOfCharacters,
1419 Text::HorizontalAlignment::Type horizontalAlignment,
1420 Vector<LineRun>& lines,
1421 float& alignmentOffset,
1422 Dali::LayoutDirection::Type layoutDirection,
1423 bool matchSystemLanguageDirection )
1425 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1427 alignmentOffset = MAX_FLOAT;
1428 // Traverse all lines and align the glyphs.
1429 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1433 LineRun& line = *it;
1435 if( line.characterRun.characterIndex < startIndex )
1437 // Do not align lines which have already been aligned.
1441 if( line.characterRun.characterIndex > lastCharacterPlusOne )
1443 // Do not align lines beyond the last laid-out character.
1447 if( line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast( lines, it ) )
1449 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1453 // Calculate the line's alignment offset accordingly with the align option,
1454 // the box width, line length, and the paragraph's direction.
1455 CalculateHorizontalAlignment( size.width,
1456 horizontalAlignment,
1459 matchSystemLanguageDirection );
1461 // Updates the alignment offset.
1462 alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1466 void CalculateHorizontalAlignment( float boxWidth,
1467 HorizontalAlignment::Type horizontalAlignment,
1469 Dali::LayoutDirection::Type layoutDirection,
1470 bool matchSystemLanguageDirection )
1472 line.alignmentOffset = 0.f;
1473 const bool isLineRTL = RTL == line.direction;
1475 // Whether to swap the alignment.
1476 // 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.
1477 bool isLayoutRTL = isLineRTL;
1478 float lineLength = line.width;
1480 // match align for system language direction
1481 if( matchSystemLanguageDirection )
1483 // Swap the alignment type if the line is right to left.
1484 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1486 // Calculate the horizontal line offset.
1487 switch( horizontalAlignment )
1489 case HorizontalAlignment::BEGIN:
1495 lineLength += line.extraLength;
1498 line.alignmentOffset = boxWidth - lineLength;
1502 line.alignmentOffset = 0.f;
1506 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1507 line.alignmentOffset -= line.extraLength;
1512 case HorizontalAlignment::CENTER:
1514 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1518 line.alignmentOffset -= line.extraLength;
1521 line.alignmentOffset = std::floor( line.alignmentOffset ); // floor() avoids pixel alignment issues.
1524 case HorizontalAlignment::END:
1528 line.alignmentOffset = 0.f;
1532 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1533 line.alignmentOffset -= line.extraLength;
1540 lineLength += line.extraLength;
1543 line.alignmentOffset = boxWidth - lineLength;
1550 void Initialize( LineRun& line )
1552 line.glyphRun.glyphIndex = 0u;
1553 line.glyphRun.numberOfGlyphs = 0u;
1554 line.characterRun.characterIndex = 0u;
1555 line.characterRun.numberOfCharacters = 0u;
1557 line.ascender = 0.f;
1558 line.descender = 0.f;
1559 line.extraLength = 0.f;
1560 line.alignmentOffset = 0.f;
1561 line.direction = LTR;
1562 line.ellipsis = false;
1563 line.lineSpacing = mDefaultLineSpacing;
1568 float mDefaultLineSpacing;
1570 IntrusivePtr<Metrics> mMetrics;
1576 mImpl = new Engine::Impl();
1584 void Engine::SetMetrics( MetricsPtr& metrics )
1586 mImpl->mMetrics = metrics;
1589 void Engine::SetLayout( Type layout )
1591 mImpl->mLayout = layout;
1594 Engine::Type Engine::GetLayout() const
1596 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1597 return mImpl->mLayout;
1600 void Engine::SetCursorWidth( int width )
1602 mImpl->mCursorWidth = static_cast<float>( width );
1605 int Engine::GetCursorWidth() const
1607 return static_cast<int>( mImpl->mCursorWidth );
1610 bool Engine::LayoutText( Parameters& layoutParameters,
1612 bool elideTextEnabled,
1613 bool& isAutoScrollEnabled )
1615 return mImpl->LayoutText( layoutParameters,
1618 isAutoScrollEnabled );
1621 void Engine::Align( const Size& size,
1622 CharacterIndex startIndex,
1623 Length numberOfCharacters,
1624 Text::HorizontalAlignment::Type horizontalAlignment,
1625 Vector<LineRun>& lines,
1626 float& alignmentOffset,
1627 Dali::LayoutDirection::Type layoutDirection,
1628 bool matchSystemLanguageDirection )
1633 horizontalAlignment,
1637 matchSystemLanguageDirection );
1640 void Engine::SetDefaultLineSpacing( float lineSpacing )
1642 mImpl->mDefaultLineSpacing = lineSpacing;
1645 float Engine::GetDefaultLineSpacing() const
1647 return mImpl->mDefaultLineSpacing;
1650 } // namespace Layout
1654 } // namespace Toolkit