2 * Copyright (c) 2015 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/glyph-metrics-helper.h>
29 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
43 #if defined(DEBUG_ENABLED)
44 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
47 const float MAX_FLOAT = std::numeric_limits<float>::max();
48 const bool RTL = true;
49 const float CURSOR_WIDTH = 1.f;
51 Length CountParagraphs( const LayoutParameters& layoutParameters )
53 Length numberOfParagraphs = 0u;
55 const CharacterIndex startCharacterIndex = *( layoutParameters.glyphsToCharactersBuffer + layoutParameters.startGlyphIndex );
57 const GlyphIndex lastGlyphIndex = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs - 1u;
58 const CharacterIndex lastCharacterIndexPlusOne = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
60 for( CharacterIndex index = startCharacterIndex; index < lastCharacterIndexPlusOne; ++index )
62 if( TextAbstraction::LINE_MUST_BREAK == *( layoutParameters.lineBreakInfoBuffer + index ) )
68 return numberOfParagraphs;
74 * @brief Stores temporary layout info of the line.
82 numberOfCharacters( 0u ),
86 wsLengthEndOfLine( 0.f ),
88 descender( MAX_FLOAT )
99 numberOfCharacters = 0u;
103 wsLengthEndOfLine = 0.f;
105 descender = MAX_FLOAT;
108 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
109 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
110 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
111 Length numberOfCharacters; ///< The number of characters which fit in one line.
112 float length; ///< The addition of the advance metric of all the glyphs which fit in one line.
113 float extraBearing; ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
114 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.
115 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
116 float ascender; ///< The maximum ascender of all fonts in the line.
117 float descender; ///< The minimum descender of all fonts in the line.
120 struct LayoutEngine::Impl
123 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
124 mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
125 mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP ),
126 mCursorWidth( CURSOR_WIDTH ),
127 mEllipsisEnabled( false )
132 * @brief Updates the line ascender and descender with the metrics of a new font.
134 * @param[in] fontId The id of the new font.
135 * @param[in,out] lineLayout The line layout.
137 void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
139 Text::FontMetrics fontMetrics;
140 mMetrics->GetFontMetrics( fontId, fontMetrics );
142 // Sets the maximum ascender.
143 if( fontMetrics.ascender > lineLayout.ascender )
145 lineLayout.ascender = fontMetrics.ascender;
148 // Sets the minimum descender.
149 if( fontMetrics.descender < lineLayout.descender )
151 lineLayout.descender = fontMetrics.descender;
156 * @brief Merges a temporary line layout into the line layout.
158 * @param[in,out] lineLayout The line layout.
159 * @param[in] tmpLineLayout A temporary line layout.
161 void MergeLineLayout( LineLayout& lineLayout,
162 const LineLayout& tmpLineLayout )
164 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
165 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
166 lineLayout.length += tmpLineLayout.length;
168 if( 0.f < tmpLineLayout.length )
170 lineLayout.length += lineLayout.wsLengthEndOfLine;
172 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
176 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
179 if( tmpLineLayout.ascender > lineLayout.ascender )
181 lineLayout.ascender = tmpLineLayout.ascender;
184 if( tmpLineLayout.descender < lineLayout.descender )
186 lineLayout.descender = tmpLineLayout.descender;
191 * Retrieves the line layout for a given box width.
193 * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
194 * because the number of characters of the line is not known (one of the responsabilities of this method
195 * is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
196 * and left to right text is laid-out, it can be small differences in the line length. One solution is to
197 * reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
198 * this method calculates which are the first and last glyphs of the line (the ones that causes the
199 * differences). This is a good point to check if there is problems with the text exceeding the boundaries
200 * of the control when there is right to left text.
202 * @param[in] parameters The layout parameters.
203 * @param[out] lineLayout The line layout.
204 * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
205 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
207 void GetLineLayoutForBox( const LayoutParameters& parameters,
208 LineLayout& lineLayout,
209 CharacterDirection& paragraphDirection,
210 bool completelyFill )
212 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
213 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
214 // Stores temporary line layout which has not been added to the final line layout.
215 LineLayout tmpLineLayout;
217 const bool isMultiline = mLayout == MULTI_LINE_BOX;
219 // The last glyph to be laid-out.
220 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
222 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
223 // In the case the line starts with a right to left character, if the width is longer than the advance,
224 // the difference needs to be added to the line length.
226 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
227 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
228 lastGlyphOfParagraphPlusOne,
229 parameters.charactersPerGlyphBuffer );
231 GlyphMetrics glyphMetrics;
232 GetGlyphsMetrics( lineLayout.glyphIndex,
233 numberOfGLyphsInGroup,
235 parameters.glyphsBuffer,
238 // Set the direction of the first character of the line.
239 lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
240 const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
241 CharacterDirection previousCharacterDirection = firstCharacterDirection;
243 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
244 float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
246 float tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
248 tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor.
250 // Calculate the line height if there is no characters.
251 FontId lastFontId = glyphMetrics.fontId;
252 UpdateLineHeight( lastFontId, tmpLineLayout );
254 bool oneWordLaidOut = false;
256 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
257 glyphIndex < lastGlyphOfParagraphPlusOne; )
259 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
261 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
262 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
263 lastGlyphOfParagraphPlusOne,
264 parameters.charactersPerGlyphBuffer );
266 GlyphMetrics glyphMetrics;
267 GetGlyphsMetrics( glyphIndex,
268 numberOfGLyphsInGroup,
270 parameters.glyphsBuffer,
273 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == parameters.totalNumberOfGlyphs;
275 // Check if the font of the current glyph is the same of the previous one.
276 // If it's different the ascender and descender need to be updated.
277 if( lastFontId != glyphMetrics.fontId )
279 UpdateLineHeight( glyphMetrics.fontId, tmpLineLayout );
280 lastFontId = glyphMetrics.fontId;
283 // Get the character indices for the current glyph. The last character index is needed
284 // because there are glyphs formed by more than one character but their break info is
285 // given only for the last character.
286 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
287 const bool hasCharacters = charactersPerGlyph > 0u;
288 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
289 const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
291 // Get the line break info for the current character.
292 const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
294 // Get the word break info for the current character.
295 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
297 // Increase the number of characters.
298 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
300 // Increase the number of glyphs.
301 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
303 // Check whether is a white space.
304 const Character character = *( parameters.textBuffer + characterFirstIndex );
305 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
307 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
308 const float previousTmpLineLength = tmpLineLayout.length;
309 const float previousTmpExtraBearing = tmpExtraBearing;
310 const float previousTmpExtraWidth = tmpExtraWidth;
312 // Get the character's direction.
313 const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
315 // Increase the accumulated length.
318 // Add the length to the length of white spaces at the end of the line.
319 tmpLineLayout.wsLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
323 // Add as well any previous white space length.
324 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphMetrics.advance;
326 // An extra space may be added to the line for the first and last glyph of the line.
327 // If the bearing of the first glyph is negative, its positive value needs to be added.
328 // If the bearing plus the width of the last glyph is greater than the advance, the difference
329 // needs to be added.
331 if( characterDirection == paragraphDirection )
333 if( RTL == characterDirection )
344 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
357 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
358 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
363 if( characterDirection != previousCharacterDirection )
365 if( RTL == characterDirection )
370 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
371 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
378 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
381 else if( characterDirection == firstCharacterDirection )
383 if( RTL == characterDirection )
389 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
397 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
398 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
403 // Clear the white space length at the end of the line.
404 tmpLineLayout.wsLengthEndOfLine = 0.f;
407 // Check if the accumulated length fits in the width of the box.
408 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
409 ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
411 // Current word does not fit in the box's width.
412 if( !oneWordLaidOut || completelyFill )
414 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
416 // The word doesn't fit in the control's width. It needs to be split by character.
417 if( tmpLineLayout.numberOfGlyphs > 0u )
419 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
420 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
421 tmpLineLayout.length = previousTmpLineLength;
422 tmpExtraBearing = previousTmpExtraBearing;
423 tmpExtraWidth = previousTmpExtraWidth;
426 // Add part of the word to the line layout.
427 MergeLineLayout( lineLayout, tmpLineLayout );
431 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
434 lineLayout.extraBearing = tmpExtraBearing;
435 lineLayout.extraWidth = tmpExtraWidth;
437 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
442 if( ( isMultiline || isLastGlyph ) &&
443 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
445 // Must break the line. Update the line layout and return.
446 MergeLineLayout( lineLayout, tmpLineLayout );
448 // Set the next paragraph's direction.
450 ( NULL != parameters.characterDirectionBuffer ) )
452 paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
455 lineLayout.extraBearing = tmpExtraBearing;
456 lineLayout.extraWidth = tmpExtraWidth;
458 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
459 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
465 ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
467 oneWordLaidOut = true;
468 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
470 // Current glyph is the last one of the current word.
471 // Add the temporal layout to the current one.
472 MergeLineLayout( lineLayout, tmpLineLayout );
474 tmpLineLayout.Clear();
477 previousCharacterDirection = characterDirection;
478 glyphIndex += numberOfGLyphsInGroup;
481 lineLayout.extraBearing = tmpExtraBearing;
482 lineLayout.extraWidth = tmpExtraWidth;
484 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
488 * @brief Calculates the vertical offset to add to the new laid-out glyphs.
490 * @pre @p lineIndex must be between 0 and the number of lines (both inclusive).
492 * @param[in] lines The previously laid-out lines.
493 * @param[in] lineIndex Index to the line where the new laid-out lines are inserted.
495 * @return The vertical offset of the lines starting from the beginning to the line @p lineIndex.
497 float SetParagraphOffset( const Vector<LineRun>& lines,
498 LineIndex lineIndex )
502 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
503 endIt = lines.Begin() + lineIndex;
507 const LineRun& line = *it;
509 offset += line.ascender + -line.descender;
515 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
516 Length numberOfGlyphs,
518 Vector2* glyphPositionsBuffer )
520 // Traverse the glyphs and set the positions.
522 // Check if the x bearing of the first character is negative.
523 // If it has a negative x bearing, it will exceed the boundaries of the actor,
524 // so the penX position needs to be moved to the right.
526 const GlyphInfo& glyph = *glyphsBuffer;
527 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
529 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
531 const GlyphInfo& glyph = *( glyphsBuffer + i );
532 Vector2& position = *( glyphPositionsBuffer + i );
534 position.x = penX + glyph.xBearing;
535 position.y = penY - glyph.yBearing;
537 penX += glyph.advance;
542 * @brief Resizes the line buffer.
544 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
545 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
546 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
547 * @param[in] updateCurrentBuffer Whether the layout is updated.
549 * @return Pointer to either lines or newLines.
551 LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
552 Vector<LineRun>& newLines,
553 Length& linesCapacity,
554 bool updateCurrentBuffer )
556 LineRun* linesBuffer = NULL;
557 // Reserve more space for the next lines.
559 if( updateCurrentBuffer )
561 newLines.Resize( linesCapacity );
562 linesBuffer = newLines.Begin();
566 lines.Resize( linesCapacity );
567 linesBuffer = lines.Begin();
574 * Ellipsis a line if it exceeds the width's of the bounding box.
576 * @param[in] layoutParameters The parameters needed to layout the text.
577 * @param[in] layout The line layout.
578 * @param[in,out] layoutSize The text's layout size.
579 * @param[in,out] linesBuffer Pointer to the line's buffer.
580 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
581 * @param[in,out] numberOfLines The number of laid-out lines.
582 * @param[in] penY The vertical layout position.
583 * @param[in] currentParagraphDirection The current paragraph's direction.
585 * return Whether the line is ellipsized.
587 bool EllipsisLine( const LayoutParameters& layoutParameters,
588 const LineLayout& layout,
590 LineRun* linesBuffer,
591 Vector2* glyphPositionsBuffer,
592 Length& numberOfLines,
594 CharacterDirection currentParagraphDirection )
596 const bool ellipsis = ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
597 ( ( mLayout == SINGLE_LINE_BOX ) &&
598 ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) );
602 // Do not layout more lines if ellipsis is enabled.
604 // The last line needs to be completely filled with characters.
605 // Part of a word may be used.
607 LineRun* lineRun = NULL;
608 LineLayout ellipsisLayout;
609 if( 0u != numberOfLines )
611 // Get the last line and layout it again with the 'completelyFill' flag to true.
612 lineRun = linesBuffer + ( numberOfLines - 1u );
614 penY -= layout.ascender - lineRun->descender;
616 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
620 // At least there is space reserved for one line.
621 lineRun = linesBuffer;
623 lineRun->glyphRun.glyphIndex = 0u;
624 ellipsisLayout.glyphIndex = 0u;
629 GetLineLayoutForBox( layoutParameters,
631 currentParagraphDirection,
634 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
635 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
636 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
637 lineRun->width = ellipsisLayout.length;
638 lineRun->extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
639 lineRun->ascender = ellipsisLayout.ascender;
640 lineRun->descender = ellipsisLayout.descender;
641 lineRun->direction = !RTL;
642 lineRun->ellipsis = true;
644 layoutSize.width = layoutParameters.boundingBox.width;
645 layoutSize.height += ( lineRun->ascender + -lineRun->descender );
647 SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
648 ellipsisLayout.numberOfGlyphs,
650 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
657 * @brief Updates the text layout with a new laid-out line.
659 * @param[in] layoutParameters The parameters needed to layout the text.
660 * @param[in] layout The line layout.
661 * @param[in,out] layoutSize The text's layout size.
662 * @param[in,out] linesBuffer Pointer to the line's buffer.
663 * @param[in] index Index to the vector of glyphs.
664 * @param[in,out] numberOfLines The number of laid-out lines.
665 * @param[in] isLastLine Whether the laid-out line is the last one.
667 void UpdateTextLayout( const LayoutParameters& layoutParameters,
668 const LineLayout& layout,
670 LineRun* linesBuffer,
672 Length& numberOfLines,
675 LineRun& lineRun = *( linesBuffer + numberOfLines );
678 lineRun.glyphRun.glyphIndex = index;
679 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
680 lineRun.characterRun.characterIndex = layout.characterIndex;
681 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
682 if( isLastLine && !layoutParameters.isLastNewParagraph )
684 const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
685 if( MULTI_LINE_BOX == mLayout )
687 lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
691 lineRun.width = width;
694 lineRun.extraLength = 0.f;
698 lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
699 lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
701 lineRun.ascender = layout.ascender;
702 lineRun.descender = layout.descender;
703 lineRun.direction = !RTL;
704 lineRun.ellipsis = false;
706 // Update the actual size.
707 if( lineRun.width > layoutSize.width )
709 layoutSize.width = lineRun.width;
712 layoutSize.height += ( lineRun.ascender + -lineRun.descender );
716 * @brief Updates the text layout with the last laid-out line.
718 * @param[in] layoutParameters The parameters needed to layout the text.
719 * @param[in] layout The line layout.
720 * @param[in,out] layoutSize The text's layout size.
721 * @param[in,out] linesBuffer Pointer to the line's buffer.
722 * @param[in] index Index to the vector of glyphs.
723 * @param[in,out] numberOfLines The number of laid-out lines.
725 void UpdateTextLayout( const LayoutParameters& layoutParameters,
726 const LineLayout& layout,
728 LineRun* linesBuffer,
730 Length& numberOfLines )
732 // Need to add a new line with no characters but with height to increase the layoutSize.height
733 const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
735 Text::FontMetrics fontMetrics;
736 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
738 LineRun& lineRun = *( linesBuffer + numberOfLines );
741 lineRun.glyphRun.glyphIndex = index + layout.numberOfGlyphs;
742 lineRun.glyphRun.numberOfGlyphs = 0u;
743 lineRun.characterRun.characterIndex = layout.characterIndex + layout.numberOfCharacters;
744 lineRun.characterRun.numberOfCharacters = 0u;
746 lineRun.ascender = fontMetrics.ascender;
747 lineRun.descender = fontMetrics.descender;
748 lineRun.extraLength = 0.f;
749 lineRun.alignmentOffset = 0.f;
750 lineRun.direction = !RTL;
751 lineRun.ellipsis = false;
753 layoutSize.height += ( lineRun.ascender + -lineRun.descender );
757 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
759 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
760 * @param[in,out] layoutSize The text's layout size.
762 void UpdateLayoutSize( const Vector<LineRun>& lines,
765 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
770 const LineRun& line = *it;
772 if( line.width > layoutSize.width )
774 layoutSize.width = line.width;
777 layoutSize.height += ( line.ascender + -line.descender );
782 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
784 * @param[in] layoutParameters The parameters needed to layout the text.
785 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
786 * @param[in] characterOffset The offset to be added to the runs of characters.
787 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
789 void UpdateLineIndexOffsets( const LayoutParameters& layoutParameters,
790 Vector<LineRun>& lines,
791 Length characterOffset,
794 // Update the glyph and character runs.
795 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
802 line.glyphRun.glyphIndex = glyphOffset;
803 line.characterRun.characterIndex = characterOffset;
805 glyphOffset += line.glyphRun.numberOfGlyphs;
806 characterOffset += line.characterRun.numberOfCharacters;
810 bool LayoutText( const LayoutParameters& layoutParameters,
811 Vector<Vector2>& glyphPositions,
812 Vector<LineRun>& lines,
815 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
816 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
818 if( 0u == layoutParameters.numberOfGlyphs )
820 // Nothing to do if there are no glyphs to layout.
824 // Set the first paragraph's direction.
825 CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
827 // Whether the layout is being updated or set from scratch.
828 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
830 Vector2* glyphPositionsBuffer = NULL;
831 Vector<Vector2> newGlyphPositions;
833 LineRun* linesBuffer = NULL;
834 Vector<LineRun> newLines;
836 // Estimate the number of lines.
837 // TODO: In a next patch the paragraphs are properly managed and this can be removed.
838 Length linesCapacity = CountParagraphs( layoutParameters );
839 Length numberOfLines = 0u;
841 if( updateCurrentBuffer )
843 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
844 glyphPositionsBuffer = newGlyphPositions.Begin();
846 newLines.Resize( linesCapacity );
847 linesBuffer = newLines.Begin();
851 glyphPositionsBuffer = glyphPositions.Begin();
853 lines.Resize( linesCapacity );
854 linesBuffer = lines.Begin();
857 float penY = SetParagraphOffset( lines,
858 layoutParameters.startLineIndex );
860 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
861 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
863 CharacterDirection currentParagraphDirection = paragraphDirection;
865 // Get the layout for the line.
867 layout.glyphIndex = index;
868 GetLineLayoutForBox( layoutParameters,
873 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
874 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
875 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
876 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
877 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
879 if( 0u == layout.numberOfGlyphs )
881 // The width is too small and no characters are laid-out.
882 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
884 lines.Resize( numberOfLines );
888 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
890 penY += layout.ascender;
892 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
894 bool ellipsis = false;
895 if( mEllipsisEnabled )
897 // Does the ellipsis of the last line.
898 ellipsis = EllipsisLine( layoutParameters,
902 glyphPositionsBuffer,
905 currentParagraphDirection );
910 // No more lines to layout.
915 // Whether the last line has been laid-out.
916 const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
918 if( numberOfLines == linesCapacity )
920 // Reserve more space for the next lines.
921 linesBuffer = ResizeLinesBuffer( lines,
924 updateCurrentBuffer );
927 // Updates the current text's layout with the line's layout.
928 UpdateTextLayout( layoutParameters,
937 layoutParameters.isLastNewParagraph &&
938 ( mLayout == MULTI_LINE_BOX ) )
940 // The last character of the text is a new paragraph character.
941 // An extra line with no characters is added to increase the text's height
942 // in order to place the cursor.
944 if( numberOfLines == linesCapacity )
946 // Reserve more space for the next lines.
947 linesBuffer = ResizeLinesBuffer( lines,
950 updateCurrentBuffer );
953 UpdateTextLayout( layoutParameters,
959 } // whether to add a last line.
961 // Sets the positions of the glyphs.
962 SetGlyphPositions( layoutParameters.glyphsBuffer + index,
963 layout.numberOfGlyphs,
965 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
967 // Updates the vertical pen's position.
968 penY += -layout.descender;
970 // Increase the glyph index.
971 index += layout.numberOfGlyphs;
974 } // end for() traversing glyphs.
976 if( updateCurrentBuffer )
978 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
979 newGlyphPositions.Begin(),
980 newGlyphPositions.End() );
982 newLines.Resize( numberOfLines );
984 // Current text's layout size adds only the newly laid-out lines.
985 // Updates the layout size with the previously laid-out lines.
986 UpdateLayoutSize( lines,
989 if( 0u != newLines.Count() )
991 const LineRun& lastLine = *( newLines.End() - 1u );
993 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
994 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
996 // Update the indices of the runs before the new laid-out lines are inserted.
997 UpdateLineIndexOffsets( layoutParameters,
1002 // Insert the lines.
1003 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1010 lines.Resize( numberOfLines );
1013 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1018 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
1019 CharacterIndex startIndex,
1020 Length numberOfCharacters,
1021 Vector<Vector2>& glyphPositions )
1023 const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
1025 // Traverses the paragraphs with right to left characters.
1026 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
1028 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
1030 if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
1032 // Do not reorder the line if it has been already reordered.
1036 if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
1038 // Do not reorder the lines after the last requested character.
1042 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
1043 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
1045 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
1047 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
1049 // Traverses the characters of the right to left paragraph.
1050 for( CharacterIndex characterLogicalIndex = 0u;
1051 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1052 ++characterLogicalIndex )
1054 // Convert the character in the logical order into the character in the visual order.
1055 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
1057 // Get the number of glyphs of the character.
1058 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
1060 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1062 // Convert the character in the visual order into the glyph in the visual order.
1063 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
1065 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
1067 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
1068 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1070 position.x = penX + glyph.xBearing;
1071 penX += glyph.advance;
1077 void Align( const Size& size,
1078 CharacterIndex startIndex,
1079 Length numberOfCharacters,
1080 Vector<LineRun>& lines )
1082 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1084 // Traverse all lines and align the glyphs.
1085 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1089 LineRun& line = *it;
1091 if( line.characterRun.characterIndex < startIndex )
1093 // Do not align lines which have already been aligned.
1097 if( line.characterRun.characterIndex >= lastCharacterPlusOne )
1099 // Do not align lines beyond the last laid-out character.
1103 // Calculate the line's alignment offset accordingly with the align option,
1104 // the box width, line length, and the paragraph's direction.
1105 CalculateHorizontalAlignment( size.width,
1110 void CalculateHorizontalAlignment( float boxWidth,
1113 line.alignmentOffset = 0.f;
1114 const bool isRTL = RTL == line.direction;
1115 float lineLength = line.width;
1117 HorizontalAlignment alignment = mHorizontalAlignment;
1120 // Swap the alignment type if the line is right to left.
1123 case HORIZONTAL_ALIGN_BEGIN:
1125 alignment = HORIZONTAL_ALIGN_END;
1128 case HORIZONTAL_ALIGN_CENTER:
1133 case HORIZONTAL_ALIGN_END:
1135 alignment = HORIZONTAL_ALIGN_BEGIN;
1141 // Calculate the horizontal line offset.
1144 case HORIZONTAL_ALIGN_BEGIN:
1146 line.alignmentOffset = 0.f;
1150 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1151 line.alignmentOffset -= line.extraLength;
1155 case HORIZONTAL_ALIGN_CENTER:
1157 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1161 line.alignmentOffset -= line.extraLength;
1164 line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
1167 case HORIZONTAL_ALIGN_END:
1171 lineLength += line.extraLength;
1174 line.alignmentOffset = boxWidth - lineLength;
1180 LayoutEngine::Layout mLayout;
1181 LayoutEngine::HorizontalAlignment mHorizontalAlignment;
1182 LayoutEngine::VerticalAlignment mVerticalAlignment;
1185 IntrusivePtr<Metrics> mMetrics;
1187 bool mEllipsisEnabled:1;
1190 LayoutEngine::LayoutEngine()
1193 mImpl = new LayoutEngine::Impl();
1196 LayoutEngine::~LayoutEngine()
1201 void LayoutEngine::SetMetrics( MetricsPtr& metrics )
1203 mImpl->mMetrics = metrics;
1206 void LayoutEngine::SetLayout( Layout layout )
1208 mImpl->mLayout = layout;
1211 LayoutEngine::Layout LayoutEngine::GetLayout() const
1213 return mImpl->mLayout;
1216 void LayoutEngine::SetTextEllipsisEnabled( bool enabled )
1218 mImpl->mEllipsisEnabled = enabled;
1221 bool LayoutEngine::GetTextEllipsisEnabled() const
1223 return mImpl->mEllipsisEnabled;
1226 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
1228 mImpl->mHorizontalAlignment = alignment;
1231 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
1233 return mImpl->mHorizontalAlignment;
1236 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
1238 mImpl->mVerticalAlignment = alignment;
1241 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
1243 return mImpl->mVerticalAlignment;
1246 void LayoutEngine::SetCursorWidth( int width )
1248 mImpl->mCursorWidth = static_cast<float>( width );
1251 int LayoutEngine::GetCursorWidth() const
1253 return static_cast<int>( mImpl->mCursorWidth );
1256 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
1257 Vector<Vector2>& glyphPositions,
1258 Vector<LineRun>& lines,
1261 return mImpl->LayoutText( layoutParameters,
1267 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
1268 CharacterIndex startIndex,
1269 Length numberOfCharacters,
1270 Vector<Vector2>& glyphPositions )
1272 mImpl->ReLayoutRightToLeftLines( layoutParameters,
1278 void LayoutEngine::Align( const Size& size,
1279 CharacterIndex startIndex,
1280 Length numberOfCharacters,
1281 Vector<LineRun>& lines )
1291 } // namespace Toolkit