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>
23 #include <dali/integration-api/debug.h>
24 #include <dali/devel-api/text-abstraction/font-client.h>
27 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
28 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
29 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
30 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
51 const float MAX_FLOAT = std::numeric_limits<float>::max();
52 const bool RTL = true;
53 const float CURSOR_WIDTH = 1.f;
54 const float LINE_SPACING= 0.f;
59 * @brief Stores temporary layout info of the line.
67 numberOfCharacters( 0u ),
71 wsLengthEndOfLine( 0.f ),
73 descender( MAX_FLOAT ),
85 numberOfCharacters = 0u;
89 wsLengthEndOfLine = 0.f;
91 descender = MAX_FLOAT;
94 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
95 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
96 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
97 Length numberOfCharacters; ///< The number of characters which fit in one line.
98 float length; ///< The addition of the advance metric of all the glyphs which fit in one line.
99 float extraBearing; ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
100 float extraWidth; ///< The extra width to be added to the line's length when the bearing + width of the last glyph is greater than the advance.
101 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the 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
110 : mLayout( Layout::Engine::SINGLE_LINE_BOX ),
111 mCursorWidth( CURSOR_WIDTH ),
112 mDefaultLineSpacing( LINE_SPACING )
117 * @brief Updates the line ascender and descender with the metrics of a new font.
119 * @param[in] fontId The id of the new font.
120 * @param[in,out] lineLayout The line layout.
122 void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
124 Text::FontMetrics fontMetrics;
125 mMetrics->GetFontMetrics( fontId, fontMetrics );
127 // Sets the maximum ascender.
128 if( fontMetrics.ascender > lineLayout.ascender )
130 lineLayout.ascender = fontMetrics.ascender;
133 // Sets the minimum descender.
134 if( fontMetrics.descender < lineLayout.descender )
136 lineLayout.descender = fontMetrics.descender;
139 // set the line spacing
140 lineLayout.lineSpacing = mDefaultLineSpacing;
144 * @brief Merges a temporary line layout into the line layout.
146 * @param[in,out] lineLayout The line layout.
147 * @param[in] tmpLineLayout A temporary line layout.
149 void MergeLineLayout( LineLayout& lineLayout,
150 const LineLayout& tmpLineLayout )
152 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
153 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
154 lineLayout.length += tmpLineLayout.length;
156 if( 0.f < tmpLineLayout.length )
158 lineLayout.length += lineLayout.wsLengthEndOfLine;
160 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
164 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
167 if( tmpLineLayout.ascender > lineLayout.ascender )
169 lineLayout.ascender = tmpLineLayout.ascender;
172 if( tmpLineLayout.descender < lineLayout.descender )
174 lineLayout.descender = tmpLineLayout.descender;
179 * Retrieves the line layout for a given box width.
181 * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
182 * because the number of characters of the line is not known (one of the responsabilities of this method
183 * is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
184 * and left to right text is laid-out, it can be small differences in the line length. One solution is to
185 * reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
186 * this method calculates which are the first and last glyphs of the line (the ones that causes the
187 * differences). This is a good point to check if there is problems with the text exceeding the boundaries
188 * of the control when there is right to left text.
190 * @param[in] parameters The layout parameters.
191 * @param[out] lineLayout The line layout.
192 * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
193 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
195 void GetLineLayoutForBox( const Parameters& parameters,
196 LineLayout& lineLayout,
197 CharacterDirection& paragraphDirection,
198 bool completelyFill )
200 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
201 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
202 // Stores temporary line layout which has not been added to the final line layout.
203 LineLayout tmpLineLayout;
205 const bool isMultiline = mLayout == MULTI_LINE_BOX;
206 const bool isWordLaidOut = parameters.lineWrapMode == Text::LineWrap::WORD;
208 // The last glyph to be laid-out.
209 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
211 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
212 // In the case the line starts with a right to left character, if the width is longer than the advance,
213 // the difference needs to be added to the line length.
215 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
216 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
217 lastGlyphOfParagraphPlusOne,
218 parameters.charactersPerGlyphBuffer );
220 GlyphMetrics glyphMetrics;
221 GetGlyphsMetrics( lineLayout.glyphIndex,
222 numberOfGLyphsInGroup,
224 parameters.glyphsBuffer,
227 // Set the direction of the first character of the line.
228 lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
229 const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
230 CharacterDirection previousCharacterDirection = firstCharacterDirection;
232 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
233 float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
235 float tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
237 tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor.
239 // Calculate the line height if there is no characters.
240 FontId lastFontId = glyphMetrics.fontId;
241 UpdateLineHeight( lastFontId, tmpLineLayout );
243 bool oneWordLaidOut = false;
245 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
246 glyphIndex < lastGlyphOfParagraphPlusOne; )
248 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
250 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
251 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
252 lastGlyphOfParagraphPlusOne,
253 parameters.charactersPerGlyphBuffer );
255 GlyphMetrics glyphMetrics;
256 GetGlyphsMetrics( glyphIndex,
257 numberOfGLyphsInGroup,
259 parameters.glyphsBuffer,
262 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == parameters.totalNumberOfGlyphs;
264 // Check if the font of the current glyph is the same of the previous one.
265 // If it's different the ascender and descender need to be updated.
266 if( lastFontId != glyphMetrics.fontId )
268 UpdateLineHeight( glyphMetrics.fontId, tmpLineLayout );
269 lastFontId = glyphMetrics.fontId;
272 // Get the character indices for the current glyph. The last character index is needed
273 // because there are glyphs formed by more than one character but their break info is
274 // given only for the last character.
275 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
276 const bool hasCharacters = charactersPerGlyph > 0u;
277 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
278 const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
280 // Get the line break info for the current character.
281 const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
283 // Increase the number of characters.
284 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
286 // Increase the number of glyphs.
287 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
289 // Check whether is a white space.
290 const Character character = *( parameters.textBuffer + characterFirstIndex );
291 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
293 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
294 const float previousTmpLineLength = tmpLineLayout.length;
295 const float previousTmpExtraBearing = tmpExtraBearing;
296 const float previousTmpExtraWidth = tmpExtraWidth;
298 // Get the character's direction.
299 const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
301 // Increase the accumulated length.
304 // Add the length to the length of white spaces at the end of the line.
305 tmpLineLayout.wsLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
309 // Add as well any previous white space length.
310 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphMetrics.advance;
312 // An extra space may be added to the line for the first and last glyph of the line.
313 // If the bearing of the first glyph is negative, its positive value needs to be added.
314 // If the bearing plus the width of the last glyph is greater than the advance, the difference
315 // needs to be added.
317 if( characterDirection == paragraphDirection )
319 if( RTL == characterDirection )
330 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
343 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
344 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
349 if( characterDirection != previousCharacterDirection )
351 if( RTL == characterDirection )
356 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
357 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
364 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
367 else if( characterDirection == firstCharacterDirection )
369 if( RTL == characterDirection )
375 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
383 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
384 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
389 // Clear the white space length at the end of the line.
390 tmpLineLayout.wsLengthEndOfLine = 0.f;
393 // Check if the accumulated length fits in the width of the box.
394 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
395 ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
397 // Current word does not fit in the box's width.
398 if( !oneWordLaidOut || completelyFill )
400 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
402 // The word doesn't fit in the control's width. It needs to be split by character.
403 if( tmpLineLayout.numberOfGlyphs > 0u )
405 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
406 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
407 tmpLineLayout.length = previousTmpLineLength;
408 tmpExtraBearing = previousTmpExtraBearing;
409 tmpExtraWidth = previousTmpExtraWidth;
412 // Add part of the word to the line layout.
413 MergeLineLayout( lineLayout, tmpLineLayout );
417 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
420 lineLayout.extraBearing = tmpExtraBearing;
421 lineLayout.extraWidth = tmpExtraWidth;
423 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
428 if( ( isMultiline || isLastGlyph ) &&
429 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
431 // Must break the line. Update the line layout and return.
432 MergeLineLayout( lineLayout, tmpLineLayout );
434 // Set the next paragraph's direction.
436 ( NULL != parameters.characterDirectionBuffer ) )
438 paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
441 lineLayout.extraBearing = tmpExtraBearing;
442 lineLayout.extraWidth = tmpExtraWidth;
444 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
445 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
451 ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
453 oneWordLaidOut = isWordLaidOut;
454 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
456 // Current glyph is the last one of the current word.
457 // Add the temporal layout to the current one.
458 MergeLineLayout( lineLayout, tmpLineLayout );
460 tmpLineLayout.Clear();
463 previousCharacterDirection = characterDirection;
464 glyphIndex += numberOfGLyphsInGroup;
467 lineLayout.extraBearing = tmpExtraBearing;
468 lineLayout.extraWidth = tmpExtraWidth;
470 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
473 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
474 Length numberOfGlyphs,
476 Vector2* glyphPositionsBuffer )
478 // Traverse the glyphs and set the positions.
480 // Check if the x bearing of the first character is negative.
481 // If it has a negative x bearing, it will exceed the boundaries of the actor,
482 // so the penX position needs to be moved to the right.
484 const GlyphInfo& glyph = *glyphsBuffer;
485 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing + outlineWidth : outlineWidth;
488 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
490 const GlyphInfo& glyph = *( glyphsBuffer + i );
491 Vector2& position = *( glyphPositionsBuffer + i );
493 position.x = penX + glyph.xBearing;
494 position.y = -glyph.yBearing;
496 penX += glyph.advance;
501 * @brief Resizes the line buffer.
503 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
504 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
505 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
506 * @param[in] updateCurrentBuffer Whether the layout is updated.
508 * @return Pointer to either lines or newLines.
510 LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
511 Vector<LineRun>& newLines,
512 Length& linesCapacity,
513 bool updateCurrentBuffer )
515 LineRun* linesBuffer = NULL;
516 // Reserve more space for the next lines.
518 if( updateCurrentBuffer )
520 newLines.Resize( linesCapacity );
521 linesBuffer = newLines.Begin();
525 lines.Resize( linesCapacity );
526 linesBuffer = lines.Begin();
533 * Ellipsis a line if it exceeds the width's of the bounding box.
535 * @param[in] layoutParameters The parameters needed to layout the text.
536 * @param[in] layout The line layout.
537 * @param[in,out] layoutSize The text's layout size.
538 * @param[in,out] linesBuffer Pointer to the line's buffer.
539 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
540 * @param[in,out] numberOfLines The number of laid-out lines.
541 * @param[in] penY The vertical layout position.
542 * @param[in] currentParagraphDirection The current paragraph's direction.
544 * return Whether the line is ellipsized.
546 bool EllipsisLine( const Parameters& layoutParameters,
547 const LineLayout& layout,
549 LineRun* linesBuffer,
550 Vector2* glyphPositionsBuffer,
551 Length& numberOfLines,
553 CharacterDirection currentParagraphDirection )
555 const bool ellipsis = ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
556 ( ( mLayout == SINGLE_LINE_BOX ) &&
557 ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) );
561 // Do not layout more lines if ellipsis is enabled.
563 // The last line needs to be completely filled with characters.
564 // Part of a word may be used.
566 LineRun* lineRun = NULL;
567 LineLayout ellipsisLayout;
568 if( 0u != numberOfLines )
570 // Get the last line and layout it again with the 'completelyFill' flag to true.
571 lineRun = linesBuffer + ( numberOfLines - 1u );
573 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
575 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
579 // At least there is space reserved for one line.
580 lineRun = linesBuffer;
582 lineRun->glyphRun.glyphIndex = 0u;
583 ellipsisLayout.glyphIndex = 0u;
588 GetLineLayoutForBox( layoutParameters,
590 currentParagraphDirection,
593 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
594 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
595 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
596 lineRun->width = ellipsisLayout.length;
597 lineRun->extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
598 lineRun->ascender = ellipsisLayout.ascender;
599 lineRun->descender = ellipsisLayout.descender;
600 lineRun->direction = !RTL;
601 lineRun->ellipsis = true;
603 layoutSize.width = layoutParameters.boundingBox.width;
604 if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
606 layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
609 SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
610 ellipsisLayout.numberOfGlyphs,
611 layoutParameters.outlineWidth,
612 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
619 * @brief Updates the text layout with a new laid-out line.
621 * @param[in] layoutParameters The parameters needed to layout the text.
622 * @param[in] layout The line layout.
623 * @param[in,out] layoutSize The text's layout size.
624 * @param[in,out] linesBuffer Pointer to the line's buffer.
625 * @param[in] index Index to the vector of glyphs.
626 * @param[in,out] numberOfLines The number of laid-out lines.
627 * @param[in] isLastLine Whether the laid-out line is the last one.
629 void UpdateTextLayout( const Parameters& layoutParameters,
630 const LineLayout& layout,
632 LineRun* linesBuffer,
634 Length& numberOfLines,
637 LineRun& lineRun = *( linesBuffer + numberOfLines );
640 lineRun.glyphRun.glyphIndex = index;
641 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
642 lineRun.characterRun.characterIndex = layout.characterIndex;
643 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
644 lineRun.lineSpacing = mDefaultLineSpacing;
646 if( isLastLine && !layoutParameters.isLastNewParagraph )
648 const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
649 if( MULTI_LINE_BOX == mLayout )
651 lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
655 lineRun.width = width;
658 lineRun.extraLength = 0.f;
662 lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
663 lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
665 lineRun.ascender = layout.ascender;
666 lineRun.descender = layout.descender;
667 lineRun.direction = !RTL;
668 lineRun.ellipsis = false;
670 // Update the actual size.
671 if( lineRun.width > layoutSize.width )
673 layoutSize.width = lineRun.width;
676 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
680 * @brief Updates the text layout with the last laid-out line.
682 * @param[in] layoutParameters The parameters needed to layout the text.
683 * @param[in] characterIndex The character index of the line.
684 * @param[in] glyphIndex The glyph index of the line.
685 * @param[in,out] layoutSize The text's layout size.
686 * @param[in,out] linesBuffer Pointer to the line's buffer.
687 * @param[in,out] numberOfLines The number of laid-out lines.
689 void UpdateTextLayout( const Parameters& layoutParameters,
690 CharacterIndex characterIndex,
691 GlyphIndex glyphIndex,
693 LineRun* linesBuffer,
694 Length& numberOfLines )
696 // Need to add a new line with no characters but with height to increase the layoutSize.height
697 const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
699 Text::FontMetrics fontMetrics;
700 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
702 LineRun& lineRun = *( linesBuffer + numberOfLines );
705 lineRun.glyphRun.glyphIndex = glyphIndex;
706 lineRun.glyphRun.numberOfGlyphs = 0u;
707 lineRun.characterRun.characterIndex = characterIndex;
708 lineRun.characterRun.numberOfCharacters = 0u;
710 lineRun.ascender = fontMetrics.ascender;
711 lineRun.descender = fontMetrics.descender;
712 lineRun.extraLength = 0.f;
713 lineRun.alignmentOffset = 0.f;
714 lineRun.direction = !RTL;
715 lineRun.ellipsis = false;
716 lineRun.lineSpacing = mDefaultLineSpacing;
718 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
722 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
724 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
725 * @param[in,out] layoutSize The text's layout size.
727 void UpdateLayoutSize( const Vector<LineRun>& lines,
730 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
735 const LineRun& line = *it;
737 if( line.width > layoutSize.width )
739 layoutSize.width = line.width;
742 layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
747 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
749 * @param[in] layoutParameters The parameters needed to layout the text.
750 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
751 * @param[in] characterOffset The offset to be added to the runs of characters.
752 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
754 void UpdateLineIndexOffsets( const Parameters& layoutParameters,
755 Vector<LineRun>& lines,
756 Length characterOffset,
759 // Update the glyph and character runs.
760 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
767 line.glyphRun.glyphIndex = glyphOffset;
768 line.characterRun.characterIndex = characterOffset;
770 glyphOffset += line.glyphRun.numberOfGlyphs;
771 characterOffset += line.characterRun.numberOfCharacters;
775 bool LayoutText( const Parameters& layoutParameters,
776 Vector<Vector2>& glyphPositions,
777 Vector<LineRun>& lines,
779 bool elideTextEnabled )
781 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
782 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
784 if( 0u == layoutParameters.numberOfGlyphs )
786 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
787 if( layoutParameters.isLastNewParagraph )
789 Length numberOfLines = lines.Count();
790 if( 0u != numberOfLines )
792 const LineRun& lastLine = *( lines.End() - 1u );
794 if( 0u != lastLine.characterRun.numberOfCharacters )
796 // Need to add a new line with no characters but with height to increase the layoutSize.height
798 Initialize( newLine );
799 lines.PushBack( newLine );
801 UpdateTextLayout( layoutParameters,
802 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
803 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
811 // Calculates the layout size.
812 UpdateLayoutSize( lines,
815 // Nothing else do if there are no glyphs to layout.
819 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
821 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
822 // This extra line needs to be removed.
823 if( 0u != lines.Count() )
825 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
827 if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
828 ( lastGlyphPlusOne == layoutParameters.totalNumberOfGlyphs ) )
830 lines.Remove( lastLine );
834 // Set the first paragraph's direction.
835 CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
837 // Whether the layout is being updated or set from scratch.
838 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
840 Vector2* glyphPositionsBuffer = NULL;
841 Vector<Vector2> newGlyphPositions;
843 LineRun* linesBuffer = NULL;
844 Vector<LineRun> newLines;
846 // Estimate the number of lines.
847 Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
848 Length numberOfLines = 0u;
850 if( updateCurrentBuffer )
852 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
853 glyphPositionsBuffer = newGlyphPositions.Begin();
855 newLines.Resize( linesCapacity );
856 linesBuffer = newLines.Begin();
860 glyphPositionsBuffer = glyphPositions.Begin();
862 lines.Resize( linesCapacity );
863 linesBuffer = lines.Begin();
866 float penY = CalculateLineOffset( lines,
867 layoutParameters.startLineIndex );
869 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
871 CharacterDirection currentParagraphDirection = paragraphDirection;
873 // Get the layout for the line.
875 layout.glyphIndex = index;
876 GetLineLayoutForBox( layoutParameters,
881 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
882 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
883 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
884 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
885 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
887 if( 0u == layout.numberOfGlyphs )
889 // The width is too small and no characters are laid-out.
890 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
892 lines.Resize( numberOfLines );
896 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
898 penY += layout.ascender;
900 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
902 bool ellipsis = false;
903 if( elideTextEnabled )
905 // Does the ellipsis of the last line.
906 ellipsis = EllipsisLine( layoutParameters,
910 glyphPositionsBuffer,
913 currentParagraphDirection );
918 // No more lines to layout.
923 // Whether the last line has been laid-out.
924 const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
926 if( numberOfLines == linesCapacity )
928 // Reserve more space for the next lines.
929 linesBuffer = ResizeLinesBuffer( lines,
932 updateCurrentBuffer );
935 // Updates the current text's layout with the line's layout.
936 UpdateTextLayout( layoutParameters,
944 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
946 if( ( nextIndex == layoutParameters.totalNumberOfGlyphs ) &&
947 layoutParameters.isLastNewParagraph &&
948 ( mLayout == MULTI_LINE_BOX ) )
950 // The last character of the text is a new paragraph character.
951 // An extra line with no characters is added to increase the text's height
952 // in order to place the cursor.
954 if( numberOfLines == linesCapacity )
956 // Reserve more space for the next lines.
957 linesBuffer = ResizeLinesBuffer( lines,
960 updateCurrentBuffer );
963 UpdateTextLayout( layoutParameters,
964 layout.characterIndex + layout.numberOfCharacters,
965 index + layout.numberOfGlyphs,
969 } // whether to add a last line.
971 // Sets the positions of the glyphs.
972 SetGlyphPositions( layoutParameters.glyphsBuffer + index,
973 layout.numberOfGlyphs,
974 layoutParameters.outlineWidth,
975 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
977 // Updates the vertical pen's position.
978 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
980 // Increase the glyph index.
983 } // end for() traversing glyphs.
985 if( updateCurrentBuffer )
987 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
988 newGlyphPositions.Begin(),
989 newGlyphPositions.End() );
990 glyphPositions.Resize( layoutParameters.totalNumberOfGlyphs );
992 newLines.Resize( numberOfLines );
994 // Current text's layout size adds only the newly laid-out lines.
995 // Updates the layout size with the previously laid-out lines.
996 UpdateLayoutSize( lines,
999 if( 0u != newLines.Count() )
1001 const LineRun& lastLine = *( newLines.End() - 1u );
1003 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1004 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1006 // Update the indices of the runs before the new laid-out lines are inserted.
1007 UpdateLineIndexOffsets( layoutParameters,
1012 // Insert the lines.
1013 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1020 lines.Resize( numberOfLines );
1023 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1028 void ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1029 CharacterIndex startIndex,
1030 Length numberOfCharacters,
1031 Vector<Vector2>& glyphPositions )
1033 const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
1035 // Traverses the paragraphs with right to left characters.
1036 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
1038 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
1040 if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
1042 // Do not reorder the line if it has been already reordered.
1046 if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
1048 // Do not reorder the lines after the last requested character.
1052 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
1053 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
1055 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing - layoutParameters.outlineWidth : -layoutParameters.outlineWidth;
1057 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
1059 // Traverses the characters of the right to left paragraph.
1060 for( CharacterIndex characterLogicalIndex = 0u;
1061 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1062 ++characterLogicalIndex )
1064 // Convert the character in the logical order into the character in the visual order.
1065 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
1067 // Get the number of glyphs of the character.
1068 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
1070 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1072 // Convert the character in the visual order into the glyph in the visual order.
1073 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
1075 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
1077 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
1078 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1080 position.x = penX + glyph.xBearing;
1081 penX += glyph.advance;
1087 void Align( const Size& size,
1088 CharacterIndex startIndex,
1089 Length numberOfCharacters,
1090 Text::HorizontalAlignment::Type horizontalAlignment,
1091 Vector<LineRun>& lines,
1092 float& alignmentOffset )
1094 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1096 alignmentOffset = MAX_FLOAT;
1097 // Traverse all lines and align the glyphs.
1098 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1102 LineRun& line = *it;
1104 if( line.characterRun.characterIndex < startIndex )
1106 // Do not align lines which have already been aligned.
1110 if( line.characterRun.characterIndex >= lastCharacterPlusOne )
1112 // Do not align lines beyond the last laid-out character.
1116 // Calculate the line's alignment offset accordingly with the align option,
1117 // the box width, line length, and the paragraph's direction.
1118 CalculateHorizontalAlignment( size.width,
1119 horizontalAlignment,
1122 // Updates the alignment offset.
1123 alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1127 void CalculateHorizontalAlignment( float boxWidth,
1128 HorizontalAlignment::Type horizontalAlignment,
1131 line.alignmentOffset = 0.f;
1132 const bool isRTL = RTL == line.direction;
1133 float lineLength = line.width;
1135 HorizontalAlignment::Type alignment = horizontalAlignment;
1138 // Swap the alignment type if the line is right to left.
1141 case HorizontalAlignment::BEGIN:
1143 alignment = HorizontalAlignment::END;
1146 case HorizontalAlignment::CENTER:
1151 case HorizontalAlignment::END:
1153 alignment = HorizontalAlignment::BEGIN;
1159 // Calculate the horizontal line offset.
1162 case HorizontalAlignment::BEGIN:
1164 line.alignmentOffset = 0.f;
1168 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1169 line.alignmentOffset -= line.extraLength;
1173 case HorizontalAlignment::CENTER:
1175 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1179 line.alignmentOffset -= line.extraLength;
1182 line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
1185 case HorizontalAlignment::END:
1189 lineLength += line.extraLength;
1192 line.alignmentOffset = boxWidth - lineLength;
1198 void Initialize( LineRun& line )
1200 line.glyphRun.glyphIndex = 0u;
1201 line.glyphRun.numberOfGlyphs = 0u;
1202 line.characterRun.characterIndex = 0u;
1203 line.characterRun.numberOfCharacters = 0u;
1205 line.ascender = 0.f;
1206 line.descender = 0.f;
1207 line.extraLength = 0.f;
1208 line.alignmentOffset = 0.f;
1209 line.direction = !RTL;
1210 line.ellipsis = false;
1211 line.lineSpacing = mDefaultLineSpacing;
1216 float mDefaultLineSpacing;
1218 IntrusivePtr<Metrics> mMetrics;
1224 mImpl = new Engine::Impl();
1232 void Engine::SetMetrics( MetricsPtr& metrics )
1234 mImpl->mMetrics = metrics;
1237 void Engine::SetLayout( Type layout )
1239 mImpl->mLayout = layout;
1242 Engine::Type Engine::GetLayout() const
1244 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1245 return mImpl->mLayout;
1248 void Engine::SetCursorWidth( int width )
1250 mImpl->mCursorWidth = static_cast<float>( width );
1253 int Engine::GetCursorWidth() const
1255 return static_cast<int>( mImpl->mCursorWidth );
1258 bool Engine::LayoutText( const Parameters& layoutParameters,
1259 Vector<Vector2>& glyphPositions,
1260 Vector<LineRun>& lines,
1262 bool elideTextEnabled )
1264 return mImpl->LayoutText( layoutParameters,
1271 void Engine::ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1272 CharacterIndex startIndex,
1273 Length numberOfCharacters,
1274 Vector<Vector2>& glyphPositions )
1276 mImpl->ReLayoutRightToLeftLines( layoutParameters,
1282 void Engine::Align( const Size& size,
1283 CharacterIndex startIndex,
1284 Length numberOfCharacters,
1285 Text::HorizontalAlignment::Type horizontalAlignment,
1286 Vector<LineRun>& lines,
1287 float& alignmentOffset )
1292 horizontalAlignment,
1297 void Engine::SetDefaultLineSpacing( float lineSpacing )
1299 mImpl->mDefaultLineSpacing = lineSpacing;
1302 float Engine::GetDefaultLineSpacing() const
1304 return mImpl->mDefaultLineSpacing;
1307 } // namespace Layout
1311 } // namespace Toolkit