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/layouts/layout-parameters.h>
28 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
47 const bool RTL = true;
48 const float CURSOR_WIDTH = 1.f;
50 Length CountParagraphs( const LayoutParameters& layoutParameters )
52 Length numberOfParagraphs = 0u;
54 const CharacterIndex startCharacterIndex = *( layoutParameters.glyphsToCharactersBuffer + layoutParameters.startGlyphIndex );
56 const GlyphIndex lastGlyphIndex = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs - 1u;
57 const CharacterIndex lastCharacterIndexPlusOne = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
59 for( CharacterIndex index = startCharacterIndex; index < lastCharacterIndexPlusOne; ++index )
61 if( TextAbstraction::LINE_MUST_BREAK == *( layoutParameters.lineBreakInfoBuffer + index ) )
67 return numberOfParagraphs;
73 * @brief Stores temporary layout info of the line.
81 numberOfCharacters( 0u ),
85 wsLengthEndOfLine( 0.f ),
87 descender( MAX_FLOAT )
98 numberOfCharacters = 0u;
102 wsLengthEndOfLine = 0.f;
104 descender = MAX_FLOAT;
107 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
108 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
109 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
110 Length numberOfCharacters; ///< The number of characters which fit in one line.
111 float length; ///< The addition of the advance metric of all the glyphs which fit in one line.
112 float extraBearing; ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
113 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.
114 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
115 float ascender; ///< The maximum ascender of all fonts in the line.
116 float descender; ///< The minimum descender of all fonts in the line.
119 struct LayoutEngine::Impl
122 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
123 mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
124 mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP ),
125 mCursorWidth( CURSOR_WIDTH ),
126 mEllipsisEnabled( false )
131 * @brief Updates the line ascender and descender with the metrics of a new font.
133 * @param[in] fontId The id of the new font.
134 * @param[in,out] lineLayout The line layout.
136 void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
138 Text::FontMetrics fontMetrics;
139 mMetrics->GetFontMetrics( fontId, fontMetrics );
141 // Sets the maximum ascender.
142 if( fontMetrics.ascender > lineLayout.ascender )
144 lineLayout.ascender = fontMetrics.ascender;
147 // Sets the minimum descender.
148 if( fontMetrics.descender < lineLayout.descender )
150 lineLayout.descender = fontMetrics.descender;
155 * @brief Merges a temporary line layout into the line layout.
157 * @param[in,out] lineLayout The line layout.
158 * @param[in] tmpLineLayout A temporary line layout.
160 void MergeLineLayout( LineLayout& lineLayout,
161 const LineLayout& tmpLineLayout )
163 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
164 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
165 lineLayout.length += tmpLineLayout.length;
167 if( 0.f < tmpLineLayout.length )
169 lineLayout.length += lineLayout.wsLengthEndOfLine;
171 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
175 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
178 if( tmpLineLayout.ascender > lineLayout.ascender )
180 lineLayout.ascender = tmpLineLayout.ascender;
183 if( tmpLineLayout.descender < lineLayout.descender )
185 lineLayout.descender = tmpLineLayout.descender;
190 * Retrieves the line layout for a given box width.
192 * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
193 * because the number of characters of the line is not known (one of the responsabilities of this method
194 * is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
195 * and left to right text is laid-out, it can be small differences in the line length. One solution is to
196 * reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
197 * this method calculates which are the first and last glyphs of the line (the ones that causes the
198 * differences). This is a good point to check if there is problems with the text exceeding the boundaries
199 * of the control when there is right to left text.
201 * @param[in] parameters The layout parameters.
202 * @param[out] lineLayout The line layout.
203 * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
204 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
206 void GetLineLayoutForBox( const LayoutParameters& parameters,
207 LineLayout& lineLayout,
208 CharacterDirection& paragraphDirection,
209 bool completelyFill )
211 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
212 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
213 // Stores temporary line layout which has not been added to the final line layout.
214 LineLayout tmpLineLayout;
216 const bool isMultiline = mLayout == MULTI_LINE_BOX;
217 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
219 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
220 // In the case the line starts with a right to left character, if the width is longer than the advance,
221 // the difference needs to be added to the line length.
222 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + lineLayout.glyphIndex );
224 // Set the direction of the first character of the line.
225 lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
226 const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
227 CharacterDirection previousCharacterDirection = firstCharacterDirection;
229 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
230 float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
232 float tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
234 tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor.
236 // Calculate the line height if there is no characters.
237 FontId lastFontId = glyphInfo.fontId;
238 UpdateLineHeight( lastFontId, tmpLineLayout );
240 bool oneWordLaidOut = false;
242 const GlyphIndex lastGlyphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
243 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
244 glyphIndex < lastGlyphPlusOne;
247 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
248 const bool isLastGlyph = glyphIndex == lastGlyphIndex;
250 // Get the glyph info.
251 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
253 // Check if the font of the current glyph is the same of the previous one.
254 // If it's different the ascender and descender need to be updated.
255 if( lastFontId != glyphInfo.fontId )
257 UpdateLineHeight( glyphInfo.fontId, tmpLineLayout );
258 lastFontId = glyphInfo.fontId;
261 // Get the character indices for the current glyph. The last character index is needed
262 // because there are glyphs formed by more than one character but their break info is
263 // given only for the last character.
264 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
265 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
266 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
268 // Get the line break info for the current character.
269 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
271 // Get the word break info for the current character.
272 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
274 // Increase the number of characters.
275 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
277 // Increase the number of glyphs.
278 tmpLineLayout.numberOfGlyphs++;
280 // Check whether is a white space.
281 const Character character = *( parameters.textBuffer + characterFirstIndex );
282 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
284 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
285 const float previousTmpLineLength = tmpLineLayout.length;
286 const float previousTmpExtraBearing = tmpExtraBearing;
287 const float previousTmpExtraWidth = tmpExtraWidth;
289 // Get the character's direction.
290 const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
292 // Increase the accumulated length.
295 // Add the length to the length of white spaces at the end of the line.
296 tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // The advance is used as the width is always zero for the white spaces.
300 // Add as well any previous white space length.
301 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
303 // An extra space may be added to the line for the first and last glyph of the line.
304 // If the bearing of the first glyph is negative, its positive value needs to be added.
305 // If the bearing plus the width of the last glyph is greater than the advance, the difference
306 // needs to be added.
308 if( characterDirection == paragraphDirection )
310 if( RTL == characterDirection )
321 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
334 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
335 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
340 if( characterDirection != previousCharacterDirection )
342 if( RTL == characterDirection )
347 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
348 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
355 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
358 else if( characterDirection == firstCharacterDirection )
360 if( RTL == characterDirection )
366 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
374 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
375 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
380 // Clear the white space length at the end of the line.
381 tmpLineLayout.wsLengthEndOfLine = 0.f;
384 // Check if the accumulated length fits in the width of the box.
385 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
386 ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
388 // Current word does not fit in the box's width.
389 if( !oneWordLaidOut || completelyFill )
391 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
393 // The word doesn't fit in the control's width. It needs to be split by character.
394 if( tmpLineLayout.numberOfGlyphs > 0u )
396 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
397 --tmpLineLayout.numberOfGlyphs;
398 tmpLineLayout.length = previousTmpLineLength;
399 tmpExtraBearing = previousTmpExtraBearing;
400 tmpExtraWidth = previousTmpExtraWidth;
403 // Add part of the word to the line layout.
404 MergeLineLayout( lineLayout, tmpLineLayout );
408 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
411 lineLayout.extraBearing = tmpExtraBearing;
412 lineLayout.extraWidth = tmpExtraWidth;
414 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
419 if( ( isMultiline || isLastGlyph ) &&
420 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
422 // Must break the line. Update the line layout and return.
423 MergeLineLayout( lineLayout, tmpLineLayout );
425 // Set the next paragraph's direction.
427 ( NULL != parameters.characterDirectionBuffer ) )
429 paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
432 lineLayout.extraBearing = tmpExtraBearing;
433 lineLayout.extraWidth = tmpExtraWidth;
435 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
436 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
441 ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
443 oneWordLaidOut = true;
444 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
446 // Current glyph is the last one of the current word.
447 // Add the temporal layout to the current one.
448 MergeLineLayout( lineLayout, tmpLineLayout );
450 tmpLineLayout.Clear();
453 previousCharacterDirection = characterDirection;
456 lineLayout.extraBearing = tmpExtraBearing;
457 lineLayout.extraWidth = tmpExtraWidth;
459 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
463 * @brief Calculates the vertical offset to add to the new laid-out glyphs.
465 * @pre @p lineIndex must be between 0 and the number of lines (both inclusive).
467 * @param[in] lines The previously laid-out lines.
468 * @param[in] lineIndex Index to the line where the new laid-out lines are inserted.
470 * @return The vertical offset of the lines starting from the beginning to the line @p lineIndex.
472 float SetParagraphOffset( const Vector<LineRun>& lines,
473 LineIndex lineIndex )
477 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
478 endIt = lines.Begin() + lineIndex;
482 const LineRun& line = *it;
484 offset += line.ascender + -line.descender;
490 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
491 Length numberOfGlyphs,
493 Vector2* glyphPositionsBuffer )
495 // Traverse the glyphs and set the positions.
497 // Check if the x bearing of the first character is negative.
498 // If it has a negative x bearing, it will exceed the boundaries of the actor,
499 // so the penX position needs to be moved to the right.
501 const GlyphInfo& glyph = *glyphsBuffer;
502 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
504 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
506 const GlyphInfo& glyph = *( glyphsBuffer + i );
507 Vector2& position = *( glyphPositionsBuffer + i );
509 position.x = penX + glyph.xBearing;
510 position.y = penY - glyph.yBearing;
512 penX += glyph.advance;
517 * @brief Resizes the line buffer.
519 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
520 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
521 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
522 * @param[in] updateCurrentBuffer Whether the layout is updated.
524 * @return Pointer to either lines or newLines.
526 LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
527 Vector<LineRun>& newLines,
528 Length& linesCapacity,
529 bool updateCurrentBuffer )
531 LineRun* linesBuffer = NULL;
532 // Reserve more space for the next lines.
534 if( updateCurrentBuffer )
536 newLines.Resize( linesCapacity );
537 linesBuffer = newLines.Begin();
541 lines.Resize( linesCapacity );
542 linesBuffer = lines.Begin();
549 * Ellipsis a line if it exceeds the width's of the bounding box.
551 * @param[in] layoutParameters The parameters needed to layout the text.
552 * @param[in] layout The line layout.
553 * @param[in,out] layoutSize The text's layout size.
554 * @param[in,out] linesBuffer Pointer to the line's buffer.
555 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
556 * @param[in,out] numberOfLines The number of laid-out lines.
557 * @param[in] penY The vertical layout position.
558 * @param[in] currentParagraphDirection The current paragraph's direction.
560 * return Whether the line is ellipsized.
562 bool EllipsisLine( const LayoutParameters& layoutParameters,
563 const LineLayout& layout,
565 LineRun* linesBuffer,
566 Vector2* glyphPositionsBuffer,
567 Length& numberOfLines,
569 CharacterDirection currentParagraphDirection )
571 const bool ellipsis = ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
572 ( ( mLayout == SINGLE_LINE_BOX ) &&
573 ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) );
577 // Do not layout more lines if ellipsis is enabled.
579 // The last line needs to be completely filled with characters.
580 // Part of a word may be used.
582 LineRun* lineRun = NULL;
583 LineLayout ellipsisLayout;
584 if( 0u != numberOfLines )
586 // Get the last line and layout it again with the 'completelyFill' flag to true.
587 lineRun = linesBuffer + ( numberOfLines - 1u );
589 penY -= layout.ascender - lineRun->descender;
591 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
595 // At least there is space reserved for one line.
596 lineRun = linesBuffer;
598 lineRun->glyphRun.glyphIndex = 0u;
599 ellipsisLayout.glyphIndex = 0u;
604 GetLineLayoutForBox( layoutParameters,
606 currentParagraphDirection,
609 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
610 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
611 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
612 lineRun->width = ellipsisLayout.length;
613 lineRun->extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
614 lineRun->ascender = ellipsisLayout.ascender;
615 lineRun->descender = ellipsisLayout.descender;
616 lineRun->direction = !RTL;
617 lineRun->ellipsis = true;
619 layoutSize.width = layoutParameters.boundingBox.width;
620 layoutSize.height += ( lineRun->ascender + -lineRun->descender );
622 SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
623 ellipsisLayout.numberOfGlyphs,
625 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
632 * @brief Updates the text layout with a new laid-out line.
634 * @param[in] layoutParameters The parameters needed to layout the text.
635 * @param[in] layout The line layout.
636 * @param[in,out] layoutSize The text's layout size.
637 * @param[in,out] linesBuffer Pointer to the line's buffer.
638 * @param[in] index Index to the vector of glyphs.
639 * @param[in,out] numberOfLines The number of laid-out lines.
640 * @param[in] isLastLine Whether the laid-out line is the last one.
642 void UpdateTextLayout( const LayoutParameters& layoutParameters,
643 const LineLayout& layout,
645 LineRun* linesBuffer,
647 Length& numberOfLines,
650 LineRun& lineRun = *( linesBuffer + numberOfLines );
653 lineRun.glyphRun.glyphIndex = index;
654 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
655 lineRun.characterRun.characterIndex = layout.characterIndex;
656 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
657 if( isLastLine && !layoutParameters.isLastNewParagraph )
659 const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
660 if( MULTI_LINE_BOX == mLayout )
662 lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
666 lineRun.width = width;
669 lineRun.extraLength = 0.f;
673 lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
674 lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
676 lineRun.ascender = layout.ascender;
677 lineRun.descender = layout.descender;
678 lineRun.direction = !RTL;
679 lineRun.ellipsis = false;
681 // Update the actual size.
682 if( lineRun.width > layoutSize.width )
684 layoutSize.width = lineRun.width;
687 layoutSize.height += ( lineRun.ascender + -lineRun.descender );
691 * @brief Updates the text layout with the last laid-out line.
693 * @param[in] layoutParameters The parameters needed to layout the text.
694 * @param[in] layout The line layout.
695 * @param[in,out] layoutSize The text's layout size.
696 * @param[in,out] linesBuffer Pointer to the line's buffer.
697 * @param[in] index Index to the vector of glyphs.
698 * @param[in,out] numberOfLines The number of laid-out lines.
700 void UpdateTextLayout( const LayoutParameters& layoutParameters,
701 const LineLayout& layout,
703 LineRun* linesBuffer,
705 Length& numberOfLines )
707 // Need to add a new line with no characters but with height to increase the layoutSize.height
708 const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
710 Text::FontMetrics fontMetrics;
711 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
713 LineRun& lineRun = *( linesBuffer + numberOfLines );
716 lineRun.glyphRun.glyphIndex = index + layout.numberOfGlyphs;
717 lineRun.glyphRun.numberOfGlyphs = 0u;
718 lineRun.characterRun.characterIndex = layout.characterIndex + layout.numberOfCharacters;
719 lineRun.characterRun.numberOfCharacters = 0u;
721 lineRun.ascender = fontMetrics.ascender;
722 lineRun.descender = fontMetrics.descender;
723 lineRun.extraLength = 0.f;
724 lineRun.alignmentOffset = 0.f;
725 lineRun.direction = !RTL;
726 lineRun.ellipsis = false;
728 layoutSize.height += ( lineRun.ascender + -lineRun.descender );
732 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
734 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
735 * @param[in,out] layoutSize The text's layout size.
737 void UpdateLayoutSize( const Vector<LineRun>& lines,
740 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
745 const LineRun& line = *it;
747 if( line.width > layoutSize.width )
749 layoutSize.width = line.width;
752 layoutSize.height += ( line.ascender + -line.descender );
757 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
759 * @param[in] layoutParameters The parameters needed to layout the text.
760 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
761 * @param[in] characterOffset The offset to be added to the runs of characters.
762 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
764 void UpdateLineIndexOffsets( const LayoutParameters& layoutParameters,
765 Vector<LineRun>& lines,
766 Length characterOffset,
769 // Update the glyph and character runs.
770 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
777 line.glyphRun.glyphIndex = glyphOffset;
778 line.characterRun.characterIndex = characterOffset;
780 glyphOffset += line.glyphRun.numberOfGlyphs;
781 characterOffset += line.characterRun.numberOfCharacters;
785 bool LayoutText( const LayoutParameters& layoutParameters,
786 Vector<Vector2>& glyphPositions,
787 Vector<LineRun>& lines,
790 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
791 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
793 if( 0u == layoutParameters.numberOfGlyphs )
795 // Nothing to do if there are no glyphs to layout.
799 // Set the first paragraph's direction.
800 CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
802 // Whether the layout is being updated or set from scratch.
803 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
805 Vector2* glyphPositionsBuffer = NULL;
806 Vector<Vector2> newGlyphPositions;
808 LineRun* linesBuffer = NULL;
809 Vector<LineRun> newLines;
811 // Estimate the number of lines.
812 // TODO: In a next patch the paragraphs are properly managed and this can be removed.
813 Length linesCapacity = CountParagraphs( layoutParameters );
814 Length numberOfLines = 0u;
816 if( updateCurrentBuffer )
818 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
819 glyphPositionsBuffer = newGlyphPositions.Begin();
821 newLines.Resize( linesCapacity );
822 linesBuffer = newLines.Begin();
826 glyphPositionsBuffer = glyphPositions.Begin();
828 lines.Resize( linesCapacity );
829 linesBuffer = lines.Begin();
832 float penY = SetParagraphOffset( lines,
833 layoutParameters.startLineIndex );
835 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
836 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
838 CharacterDirection currentParagraphDirection = paragraphDirection;
840 // Get the layout for the line.
842 layout.glyphIndex = index;
843 GetLineLayoutForBox( layoutParameters,
848 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
849 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
850 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
851 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
852 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
854 if( 0u == layout.numberOfGlyphs )
856 // The width is too small and no characters are laid-out.
857 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
859 lines.Resize( numberOfLines );
863 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
865 penY += layout.ascender;
867 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
869 bool ellipsis = false;
870 if( mEllipsisEnabled )
872 // Does the ellipsis of the last line.
873 ellipsis = EllipsisLine( layoutParameters,
877 glyphPositionsBuffer,
880 currentParagraphDirection );
885 // No more lines to layout.
890 // Whether the last line has been laid-out.
891 const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
893 if( numberOfLines == linesCapacity )
895 // Reserve more space for the next lines.
896 linesBuffer = ResizeLinesBuffer( lines,
899 updateCurrentBuffer );
902 // Updates the current text's layout with the line's layout.
903 UpdateTextLayout( layoutParameters,
912 layoutParameters.isLastNewParagraph &&
913 ( mLayout == MULTI_LINE_BOX ) )
915 // The last character of the text is a new paragraph character.
916 // An extra line with no characters is added to increase the text's height
917 // in order to place the cursor.
919 if( numberOfLines == linesCapacity )
921 // Reserve more space for the next lines.
922 linesBuffer = ResizeLinesBuffer( lines,
925 updateCurrentBuffer );
928 UpdateTextLayout( layoutParameters,
934 } // whether to add a last line.
936 // Sets the positions of the glyphs.
937 SetGlyphPositions( layoutParameters.glyphsBuffer + index,
938 layout.numberOfGlyphs,
940 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
942 // Updates the vertical pen's position.
943 penY += -layout.descender;
945 // Increase the glyph index.
946 index += layout.numberOfGlyphs;
949 } // end for() traversing glyphs.
951 if( updateCurrentBuffer )
953 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
954 newGlyphPositions.Begin(),
955 newGlyphPositions.End() );
957 newLines.Resize( numberOfLines );
959 // Current text's layout size adds only the newly laid-out lines.
960 // Updates the layout size with the previously laid-out lines.
961 UpdateLayoutSize( lines,
964 if( 0u != newLines.Count() )
966 const LineRun& lastLine = *( newLines.End() - 1u );
968 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
969 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
971 // Update the indices of the runs before the new laid-out lines are inserted.
972 UpdateLineIndexOffsets( layoutParameters,
978 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
985 lines.Resize( numberOfLines );
988 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
993 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
994 CharacterIndex startIndex,
995 Length numberOfCharacters,
996 Vector<Vector2>& glyphPositions )
998 const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
1000 // Traverses the paragraphs with right to left characters.
1001 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
1003 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
1005 if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
1007 // Do not reorder the line if it has been already reordered.
1011 if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
1013 // Do not reorder the lines after the last requested character.
1017 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
1018 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
1020 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
1022 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
1024 // Traverses the characters of the right to left paragraph.
1025 for( CharacterIndex characterLogicalIndex = 0u;
1026 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1027 ++characterLogicalIndex )
1029 // Convert the character in the logical order into the character in the visual order.
1030 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
1032 // Get the number of glyphs of the character.
1033 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
1035 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1037 // Convert the character in the visual order into the glyph in the visual order.
1038 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
1040 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
1042 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
1043 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1045 position.x = penX + glyph.xBearing;
1046 penX += glyph.advance;
1052 void Align( const Size& size,
1053 CharacterIndex startIndex,
1054 Length numberOfCharacters,
1055 Vector<LineRun>& lines )
1057 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1059 // Traverse all lines and align the glyphs.
1060 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1064 LineRun& line = *it;
1066 if( line.characterRun.characterIndex < startIndex )
1068 // Do not align lines which have already been aligned.
1072 if( line.characterRun.characterIndex >= lastCharacterPlusOne )
1074 // Do not align lines beyond the last laid-out character.
1078 // Calculate the line's alignment offset accordingly with the align option,
1079 // the box width, line length, and the paragraph's direction.
1080 CalculateHorizontalAlignment( size.width,
1085 void CalculateHorizontalAlignment( float boxWidth,
1088 line.alignmentOffset = 0.f;
1089 const bool isRTL = RTL == line.direction;
1090 float lineLength = line.width;
1092 HorizontalAlignment alignment = mHorizontalAlignment;
1095 // Swap the alignment type if the line is right to left.
1098 case HORIZONTAL_ALIGN_BEGIN:
1100 alignment = HORIZONTAL_ALIGN_END;
1103 case HORIZONTAL_ALIGN_CENTER:
1108 case HORIZONTAL_ALIGN_END:
1110 alignment = HORIZONTAL_ALIGN_BEGIN;
1116 // Calculate the horizontal line offset.
1119 case HORIZONTAL_ALIGN_BEGIN:
1121 line.alignmentOffset = 0.f;
1125 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1126 line.alignmentOffset -= line.extraLength;
1130 case HORIZONTAL_ALIGN_CENTER:
1132 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1136 line.alignmentOffset -= line.extraLength;
1139 line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
1142 case HORIZONTAL_ALIGN_END:
1146 lineLength += line.extraLength;
1149 line.alignmentOffset = boxWidth - lineLength;
1155 LayoutEngine::Layout mLayout;
1156 LayoutEngine::HorizontalAlignment mHorizontalAlignment;
1157 LayoutEngine::VerticalAlignment mVerticalAlignment;
1160 IntrusivePtr<Metrics> mMetrics;
1162 bool mEllipsisEnabled:1;
1165 LayoutEngine::LayoutEngine()
1168 mImpl = new LayoutEngine::Impl();
1171 LayoutEngine::~LayoutEngine()
1176 void LayoutEngine::SetMetrics( MetricsPtr& metrics )
1178 mImpl->mMetrics = metrics;
1181 void LayoutEngine::SetLayout( Layout layout )
1183 mImpl->mLayout = layout;
1186 LayoutEngine::Layout LayoutEngine::GetLayout() const
1188 return mImpl->mLayout;
1191 void LayoutEngine::SetTextEllipsisEnabled( bool enabled )
1193 mImpl->mEllipsisEnabled = enabled;
1196 bool LayoutEngine::GetTextEllipsisEnabled() const
1198 return mImpl->mEllipsisEnabled;
1201 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
1203 mImpl->mHorizontalAlignment = alignment;
1206 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
1208 return mImpl->mHorizontalAlignment;
1211 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
1213 mImpl->mVerticalAlignment = alignment;
1216 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
1218 return mImpl->mVerticalAlignment;
1221 void LayoutEngine::SetCursorWidth( int width )
1223 mImpl->mCursorWidth = static_cast<float>( width );
1226 int LayoutEngine::GetCursorWidth() const
1228 return static_cast<int>( mImpl->mCursorWidth );
1231 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
1232 Vector<Vector2>& glyphPositions,
1233 Vector<LineRun>& lines,
1236 return mImpl->LayoutText( layoutParameters,
1242 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
1243 CharacterIndex startIndex,
1244 Length numberOfCharacters,
1245 Vector<Vector2>& glyphPositions )
1247 mImpl->ReLayoutRightToLeftLines( layoutParameters,
1253 void LayoutEngine::Align( const Size& size,
1254 CharacterIndex startIndex,
1255 Length numberOfCharacters,
1256 Vector<LineRun>& lines )
1266 } // namespace Toolkit