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>
31 #include <dali-toolkit/internal/text/character-set-conversion.h>
48 #if defined(DEBUG_ENABLED)
49 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
52 const float MAX_FLOAT = std::numeric_limits<float>::max();
53 const bool RTL = true;
54 const float CURSOR_WIDTH = 1.f;
55 const float LINE_SPACING= 0.f;
60 * @brief Stores temporary layout info of the line.
68 numberOfCharacters( 0u ),
72 wsLengthEndOfLine( 0.f ),
74 descender( MAX_FLOAT ),
86 numberOfCharacters = 0u;
90 wsLengthEndOfLine = 0.f;
92 descender = MAX_FLOAT;
95 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
96 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
97 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
98 Length numberOfCharacters; ///< The number of characters which fit in one line.
99 float length; ///< The addition of the advance metric of all the glyphs which fit in one line.
100 float extraBearing; ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
101 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.
102 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
103 float ascender; ///< The maximum ascender of all fonts in the line.
104 float descender; ///< The minimum descender of all fonts in the line.
105 float lineSpacing; ///< The line spacing
111 : mLayout( Layout::Engine::SINGLE_LINE_BOX ),
112 mCursorWidth( CURSOR_WIDTH ),
113 mDefaultLineSpacing( LINE_SPACING ),
114 mPreviousCharacterExtraWidth( 0.0f )
119 * @brief Updates the line ascender and descender with the metrics of a new font.
121 * @param[in] fontId The id of the new font.
122 * @param[in,out] lineLayout The line layout.
124 void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
126 Text::FontMetrics fontMetrics;
127 mMetrics->GetFontMetrics( fontId, fontMetrics );
129 // Sets the maximum ascender.
130 if( fontMetrics.ascender > lineLayout.ascender )
132 lineLayout.ascender = fontMetrics.ascender;
135 // Sets the minimum descender.
136 if( fontMetrics.descender < lineLayout.descender )
138 lineLayout.descender = fontMetrics.descender;
141 // set the line spacing
142 lineLayout.lineSpacing = mDefaultLineSpacing;
146 * @brief Merges a temporary line layout into the line layout.
148 * @param[in,out] lineLayout The line layout.
149 * @param[in] tmpLineLayout A temporary line layout.
151 void MergeLineLayout( LineLayout& lineLayout,
152 const LineLayout& tmpLineLayout )
154 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
155 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
156 lineLayout.length += tmpLineLayout.length;
158 if( 0.f < tmpLineLayout.length )
160 lineLayout.length += lineLayout.wsLengthEndOfLine;
162 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
166 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
169 if( tmpLineLayout.ascender > lineLayout.ascender )
171 lineLayout.ascender = tmpLineLayout.ascender;
174 if( tmpLineLayout.descender < lineLayout.descender )
176 lineLayout.descender = tmpLineLayout.descender;
181 * Retrieves the line layout for a given box width.
183 * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
184 * because the number of characters of the line is not known (one of the responsabilities of this method
185 * is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
186 * and left to right text is laid-out, it can be small differences in the line length. One solution is to
187 * reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
188 * this method calculates which are the first and last glyphs of the line (the ones that causes the
189 * differences). This is a good point to check if there is problems with the text exceeding the boundaries
190 * of the control when there is right to left text.
192 * @param[in] parameters The layout parameters.
193 * @param[out] lineLayout The line layout.
194 * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
195 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
197 void GetLineLayoutForBox( const Parameters& parameters,
198 LineLayout& lineLayout,
199 CharacterDirection& paragraphDirection,
200 bool completelyFill )
202 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
203 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
204 // Stores temporary line layout which has not been added to the final line layout.
205 LineLayout tmpLineLayout;
207 const bool isMultiline = mLayout == MULTI_LINE_BOX;
208 const bool isWordLaidOut = parameters.lineWrapMode == Text::LineWrap::WORD;
210 // The last glyph to be laid-out.
211 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
213 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
214 // In the case the line starts with a right to left character, if the width is longer than the advance,
215 // the difference needs to be added to the line length.
217 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
218 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
219 lastGlyphOfParagraphPlusOne,
220 parameters.charactersPerGlyphBuffer );
222 GlyphMetrics glyphMetrics;
223 GetGlyphsMetrics( lineLayout.glyphIndex,
224 numberOfGLyphsInGroup,
226 parameters.glyphsBuffer,
229 // Set the direction of the first character of the line.
230 lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
231 const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
232 CharacterDirection previousCharacterDirection = firstCharacterDirection;
234 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
235 float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
237 float tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
239 tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor.
241 // Calculate the line height if there is no characters.
242 FontId lastFontId = glyphMetrics.fontId;
243 UpdateLineHeight( lastFontId, tmpLineLayout );
245 bool oneWordLaidOut = false;
247 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
248 glyphIndex < lastGlyphOfParagraphPlusOne; )
250 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
252 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
253 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
254 lastGlyphOfParagraphPlusOne,
255 parameters.charactersPerGlyphBuffer );
257 GlyphMetrics glyphMetrics;
258 GetGlyphsMetrics( glyphIndex,
259 numberOfGLyphsInGroup,
261 parameters.glyphsBuffer,
264 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == parameters.totalNumberOfGlyphs;
266 // Check if the font of the current glyph is the same of the previous one.
267 // If it's different the ascender and descender need to be updated.
268 if( lastFontId != glyphMetrics.fontId )
270 UpdateLineHeight( glyphMetrics.fontId, tmpLineLayout );
271 lastFontId = glyphMetrics.fontId;
274 // Get the character indices for the current glyph. The last character index is needed
275 // because there are glyphs formed by more than one character but their break info is
276 // given only for the last character.
277 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
278 const bool hasCharacters = charactersPerGlyph > 0u;
279 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
280 const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
282 // Get the line break info for the current character.
283 const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
285 // Increase the number of characters.
286 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
288 // Increase the number of glyphs.
289 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
291 // Check whether is a white space.
292 const Character character = *( parameters.textBuffer + characterFirstIndex );
293 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
295 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
296 const float previousTmpLineLength = tmpLineLayout.length;
297 const float previousTmpExtraBearing = tmpExtraBearing;
298 const float previousTmpExtraWidth = tmpExtraWidth;
300 // Get the character's direction.
301 const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
303 // Increase the accumulated length.
306 // Add the length to the length of white spaces at the end of the line.
307 tmpLineLayout.wsLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
311 // Add as well any previous white space length.
312 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphMetrics.advance;
314 // An extra space may be added to the line for the first and last glyph of the line.
315 // If the bearing of the first glyph is negative, its positive value needs to be added.
316 // If the bearing plus the width of the last glyph is greater than the advance, the difference
317 // needs to be added.
319 if( characterDirection == paragraphDirection )
321 if( RTL == characterDirection )
332 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
345 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
346 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
347 tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
352 if( characterDirection != previousCharacterDirection )
354 if( RTL == characterDirection )
359 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
360 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
361 tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
368 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
371 else if( characterDirection == firstCharacterDirection )
373 if( RTL == characterDirection )
379 tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
387 const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
388 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
389 tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
394 // Clear the white space length at the end of the line.
395 tmpLineLayout.wsLengthEndOfLine = 0.f;
398 // Save the current extra width to compare with the next one
399 mPreviousCharacterExtraWidth = tmpExtraWidth;
401 // Check if the accumulated length fits in the width of the box.
402 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
403 ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
405 // Current word does not fit in the box's width.
406 if( !oneWordLaidOut || completelyFill )
408 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
410 // The word doesn't fit in the control's width. It needs to be split by character.
411 if( tmpLineLayout.numberOfGlyphs > 0u )
413 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
414 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
415 tmpLineLayout.length = previousTmpLineLength;
416 tmpExtraBearing = previousTmpExtraBearing;
417 tmpExtraWidth = previousTmpExtraWidth;
420 // Add part of the word to the line layout.
421 MergeLineLayout( lineLayout, tmpLineLayout );
425 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
428 lineLayout.extraBearing = tmpExtraBearing;
429 lineLayout.extraWidth = tmpExtraWidth;
431 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
436 if( ( isMultiline || isLastGlyph ) &&
437 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
439 // Must break the line. Update the line layout and return.
440 MergeLineLayout( lineLayout, tmpLineLayout );
442 // Set the next paragraph's direction.
444 ( NULL != parameters.characterDirectionBuffer ) )
446 paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
449 lineLayout.extraBearing = tmpExtraBearing;
450 lineLayout.extraWidth = tmpExtraWidth;
452 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
453 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
459 ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
461 oneWordLaidOut = isWordLaidOut;
462 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
464 // Current glyph is the last one of the current word.
465 // Add the temporal layout to the current one.
466 MergeLineLayout( lineLayout, tmpLineLayout );
468 tmpLineLayout.Clear();
471 previousCharacterDirection = characterDirection;
472 glyphIndex += numberOfGLyphsInGroup;
475 lineLayout.extraBearing = tmpExtraBearing;
476 lineLayout.extraWidth = tmpExtraWidth;
478 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
481 void SetGlyphPositions( const Parameters& layoutParameters,
482 const GlyphInfo* const glyphsBuffer,
483 Length numberOfGlyphs,
485 Vector2* glyphPositionsBuffer )
487 // Traverse the glyphs and set the positions.
489 // Check if the x bearing of the first character is negative.
490 // If it has a negative x bearing, it will exceed the boundaries of the actor,
491 // so the penX position needs to be moved to the right.
493 const GlyphInfo& glyph = *glyphsBuffer;
494 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing + outlineWidth : outlineWidth;
497 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
499 const CharacterIndex characterIndex = *( layoutParameters.glyphsToCharactersBuffer + i );
500 const Character character = *( layoutParameters.textBuffer + characterIndex );
502 Utf32ToUtf8( reinterpret_cast<const uint32_t* const>( &character ), 1, text );
504 const GlyphInfo& glyph = *( glyphsBuffer + i );
505 Vector2& position = *( glyphPositionsBuffer + i );
507 position.x = penX + glyph.xBearing;
508 position.y = -glyph.yBearing;
510 penX += glyph.advance;
512 DALI_LOG_RELEASE_INFO("SetGlyphPositions: text: %s, GlyphIndex: %u, glyph.index: %u, glyph.xBearing: %f, glyph.advance: %f, position.x: %f, penX: %f\n", text.c_str(), i, glyph.index, glyph.xBearing, glyph.advance, position.x, penX);
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 Parameters& 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 + lineRun->lineSpacing;
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 if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
622 layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
625 SetGlyphPositions( layoutParameters, layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
626 ellipsisLayout.numberOfGlyphs,
627 layoutParameters.outlineWidth,
628 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
635 * @brief Updates the text layout with a new laid-out line.
637 * @param[in] layoutParameters The parameters needed to layout the text.
638 * @param[in] layout The line layout.
639 * @param[in,out] layoutSize The text's layout size.
640 * @param[in,out] linesBuffer Pointer to the line's buffer.
641 * @param[in] index Index to the vector of glyphs.
642 * @param[in,out] numberOfLines The number of laid-out lines.
643 * @param[in] isLastLine Whether the laid-out line is the last one.
645 void UpdateTextLayout( const Parameters& layoutParameters,
646 const LineLayout& layout,
648 LineRun* linesBuffer,
650 Length& numberOfLines,
653 LineRun& lineRun = *( linesBuffer + numberOfLines );
656 lineRun.glyphRun.glyphIndex = index;
657 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
658 lineRun.characterRun.characterIndex = layout.characterIndex;
659 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
660 lineRun.lineSpacing = mDefaultLineSpacing;
662 if( isLastLine && !layoutParameters.isLastNewParagraph )
664 const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
665 if( MULTI_LINE_BOX == mLayout )
667 lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
671 lineRun.width = width;
674 lineRun.extraLength = 0.f;
678 lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
679 lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
681 lineRun.ascender = layout.ascender;
682 lineRun.descender = layout.descender;
683 lineRun.direction = !RTL;
684 lineRun.ellipsis = false;
686 // Update the actual size.
687 if( lineRun.width > layoutSize.width )
689 layoutSize.width = lineRun.width;
692 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
696 * @brief Updates the text layout with the last laid-out line.
698 * @param[in] layoutParameters The parameters needed to layout the text.
699 * @param[in] characterIndex The character index of the line.
700 * @param[in] glyphIndex The glyph index of the line.
701 * @param[in,out] layoutSize The text's layout size.
702 * @param[in,out] linesBuffer Pointer to the line's buffer.
703 * @param[in,out] numberOfLines The number of laid-out lines.
705 void UpdateTextLayout( const Parameters& layoutParameters,
706 CharacterIndex characterIndex,
707 GlyphIndex glyphIndex,
709 LineRun* linesBuffer,
710 Length& numberOfLines )
712 // Need to add a new line with no characters but with height to increase the layoutSize.height
713 const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
715 Text::FontMetrics fontMetrics;
716 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
718 LineRun& lineRun = *( linesBuffer + numberOfLines );
721 lineRun.glyphRun.glyphIndex = glyphIndex;
722 lineRun.glyphRun.numberOfGlyphs = 0u;
723 lineRun.characterRun.characterIndex = characterIndex;
724 lineRun.characterRun.numberOfCharacters = 0u;
726 lineRun.ascender = fontMetrics.ascender;
727 lineRun.descender = fontMetrics.descender;
728 lineRun.extraLength = 0.f;
729 lineRun.alignmentOffset = 0.f;
730 lineRun.direction = !RTL;
731 lineRun.ellipsis = false;
732 lineRun.lineSpacing = mDefaultLineSpacing;
734 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
738 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
740 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
741 * @param[in,out] layoutSize The text's layout size.
743 void UpdateLayoutSize( const Vector<LineRun>& lines,
746 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
751 const LineRun& line = *it;
753 if( line.width > layoutSize.width )
755 layoutSize.width = line.width;
758 layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
763 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
765 * @param[in] layoutParameters The parameters needed to layout the text.
766 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
767 * @param[in] characterOffset The offset to be added to the runs of characters.
768 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
770 void UpdateLineIndexOffsets( const Parameters& layoutParameters,
771 Vector<LineRun>& lines,
772 Length characterOffset,
775 // Update the glyph and character runs.
776 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
783 line.glyphRun.glyphIndex = glyphOffset;
784 line.characterRun.characterIndex = characterOffset;
786 glyphOffset += line.glyphRun.numberOfGlyphs;
787 characterOffset += line.characterRun.numberOfCharacters;
791 bool LayoutText( const Parameters& layoutParameters,
792 Vector<Vector2>& glyphPositions,
793 Vector<LineRun>& lines,
795 bool elideTextEnabled )
797 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
798 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
800 if( 0u == layoutParameters.numberOfGlyphs )
802 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
803 if( layoutParameters.isLastNewParagraph )
805 Length numberOfLines = lines.Count();
806 if( 0u != numberOfLines )
808 const LineRun& lastLine = *( lines.End() - 1u );
810 if( 0u != lastLine.characterRun.numberOfCharacters )
812 // Need to add a new line with no characters but with height to increase the layoutSize.height
814 Initialize( newLine );
815 lines.PushBack( newLine );
817 UpdateTextLayout( layoutParameters,
818 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
819 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
827 // Calculates the layout size.
828 UpdateLayoutSize( lines,
831 // Nothing else do if there are no glyphs to layout.
835 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
837 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
838 // This extra line needs to be removed.
839 if( 0u != lines.Count() )
841 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
843 if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
844 ( lastGlyphPlusOne == layoutParameters.totalNumberOfGlyphs ) )
846 lines.Remove( lastLine );
850 // Set the first paragraph's direction.
851 CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
853 // Whether the layout is being updated or set from scratch.
854 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
856 Vector2* glyphPositionsBuffer = NULL;
857 Vector<Vector2> newGlyphPositions;
859 LineRun* linesBuffer = NULL;
860 Vector<LineRun> newLines;
862 // Estimate the number of lines.
863 Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
864 Length numberOfLines = 0u;
866 if( updateCurrentBuffer )
868 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
869 glyphPositionsBuffer = newGlyphPositions.Begin();
871 newLines.Resize( linesCapacity );
872 linesBuffer = newLines.Begin();
876 glyphPositionsBuffer = glyphPositions.Begin();
878 lines.Resize( linesCapacity );
879 linesBuffer = lines.Begin();
882 float penY = CalculateLineOffset( lines,
883 layoutParameters.startLineIndex );
885 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
887 CharacterDirection currentParagraphDirection = paragraphDirection;
889 // Get the layout for the line.
891 layout.glyphIndex = index;
892 GetLineLayoutForBox( layoutParameters,
897 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
898 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
899 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
900 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
901 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
903 if( 0u == layout.numberOfGlyphs )
905 // The width is too small and no characters are laid-out.
906 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
908 lines.Resize( numberOfLines );
912 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
914 penY += layout.ascender;
916 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
918 bool ellipsis = false;
919 if( elideTextEnabled )
921 // Does the ellipsis of the last line.
922 ellipsis = EllipsisLine( layoutParameters,
926 glyphPositionsBuffer,
929 currentParagraphDirection );
934 // No more lines to layout.
939 // Whether the last line has been laid-out.
940 const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
942 if( numberOfLines == linesCapacity )
944 // Reserve more space for the next lines.
945 linesBuffer = ResizeLinesBuffer( lines,
948 updateCurrentBuffer );
951 // Updates the current text's layout with the line's layout.
952 UpdateTextLayout( layoutParameters,
960 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
962 if( ( nextIndex == layoutParameters.totalNumberOfGlyphs ) &&
963 layoutParameters.isLastNewParagraph &&
964 ( mLayout == MULTI_LINE_BOX ) )
966 // The last character of the text is a new paragraph character.
967 // An extra line with no characters is added to increase the text's height
968 // in order to place the cursor.
970 if( numberOfLines == linesCapacity )
972 // Reserve more space for the next lines.
973 linesBuffer = ResizeLinesBuffer( lines,
976 updateCurrentBuffer );
979 UpdateTextLayout( layoutParameters,
980 layout.characterIndex + layout.numberOfCharacters,
981 index + layout.numberOfGlyphs,
985 } // whether to add a last line.
987 // Sets the positions of the glyphs.
988 SetGlyphPositions( layoutParameters, layoutParameters.glyphsBuffer + index,
989 layout.numberOfGlyphs,
990 layoutParameters.outlineWidth,
991 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
993 // Updates the vertical pen's position.
994 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
996 // Increase the glyph index.
999 } // end for() traversing glyphs.
1001 if( updateCurrentBuffer )
1003 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1004 newGlyphPositions.Begin(),
1005 newGlyphPositions.End() );
1006 glyphPositions.Resize( layoutParameters.totalNumberOfGlyphs );
1008 newLines.Resize( numberOfLines );
1010 // Current text's layout size adds only the newly laid-out lines.
1011 // Updates the layout size with the previously laid-out lines.
1012 UpdateLayoutSize( lines,
1015 if( 0u != newLines.Count() )
1017 const LineRun& lastLine = *( newLines.End() - 1u );
1019 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1020 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1022 // Update the indices of the runs before the new laid-out lines are inserted.
1023 UpdateLineIndexOffsets( layoutParameters,
1028 // Insert the lines.
1029 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1036 lines.Resize( numberOfLines );
1039 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1044 void ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1045 CharacterIndex startIndex,
1046 Length numberOfCharacters,
1047 Vector<Vector2>& glyphPositions )
1049 const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
1051 // Traverses the paragraphs with right to left characters.
1052 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
1054 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
1056 if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
1058 // Do not reorder the line if it has been already reordered.
1062 if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
1064 // Do not reorder the lines after the last requested character.
1068 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
1069 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
1071 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing - layoutParameters.outlineWidth : -layoutParameters.outlineWidth;
1073 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
1075 // Traverses the characters of the right to left paragraph.
1076 for( CharacterIndex characterLogicalIndex = 0u;
1077 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1078 ++characterLogicalIndex )
1080 // Convert the character in the logical order into the character in the visual order.
1081 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
1083 // Get the number of glyphs of the character.
1084 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
1086 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1088 // Convert the character in the visual order into the glyph in the visual order.
1089 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
1091 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
1093 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
1094 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1096 position.x = penX + glyph.xBearing;
1097 penX += glyph.advance;
1103 void Align( const Size& size,
1104 CharacterIndex startIndex,
1105 Length numberOfCharacters,
1106 Text::HorizontalAlignment::Type horizontalAlignment,
1107 Vector<LineRun>& lines,
1108 float& alignmentOffset )
1110 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1112 alignmentOffset = MAX_FLOAT;
1113 // Traverse all lines and align the glyphs.
1114 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1118 LineRun& line = *it;
1120 if( line.characterRun.characterIndex < startIndex )
1122 // Do not align lines which have already been aligned.
1126 if( line.characterRun.characterIndex >= lastCharacterPlusOne )
1128 // Do not align lines beyond the last laid-out character.
1132 // Calculate the line's alignment offset accordingly with the align option,
1133 // the box width, line length, and the paragraph's direction.
1134 CalculateHorizontalAlignment( size.width,
1135 horizontalAlignment,
1138 // Updates the alignment offset.
1139 alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1143 void CalculateHorizontalAlignment( float boxWidth,
1144 HorizontalAlignment::Type horizontalAlignment,
1147 line.alignmentOffset = 0.f;
1148 const bool isRTL = RTL == line.direction;
1149 float lineLength = line.width;
1151 HorizontalAlignment::Type alignment = horizontalAlignment;
1154 // Swap the alignment type if the line is right to left.
1157 case HorizontalAlignment::BEGIN:
1159 alignment = HorizontalAlignment::END;
1162 case HorizontalAlignment::CENTER:
1167 case HorizontalAlignment::END:
1169 alignment = HorizontalAlignment::BEGIN;
1175 // Calculate the horizontal line offset.
1178 case HorizontalAlignment::BEGIN:
1180 line.alignmentOffset = 0.f;
1184 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1185 line.alignmentOffset -= line.extraLength;
1189 case HorizontalAlignment::CENTER:
1191 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1195 line.alignmentOffset -= line.extraLength;
1198 line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
1201 case HorizontalAlignment::END:
1205 lineLength += line.extraLength;
1208 line.alignmentOffset = boxWidth - lineLength;
1214 void Initialize( LineRun& line )
1216 line.glyphRun.glyphIndex = 0u;
1217 line.glyphRun.numberOfGlyphs = 0u;
1218 line.characterRun.characterIndex = 0u;
1219 line.characterRun.numberOfCharacters = 0u;
1221 line.ascender = 0.f;
1222 line.descender = 0.f;
1223 line.extraLength = 0.f;
1224 line.alignmentOffset = 0.f;
1225 line.direction = !RTL;
1226 line.ellipsis = false;
1227 line.lineSpacing = mDefaultLineSpacing;
1232 float mDefaultLineSpacing;
1233 float mPreviousCharacterExtraWidth;
1235 IntrusivePtr<Metrics> mMetrics;
1241 mImpl = new Engine::Impl();
1249 void Engine::SetMetrics( MetricsPtr& metrics )
1251 mImpl->mMetrics = metrics;
1254 void Engine::SetLayout( Type layout )
1256 mImpl->mLayout = layout;
1259 Engine::Type Engine::GetLayout() const
1261 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1262 return mImpl->mLayout;
1265 void Engine::SetCursorWidth( int width )
1267 mImpl->mCursorWidth = static_cast<float>( width );
1270 int Engine::GetCursorWidth() const
1272 return static_cast<int>( mImpl->mCursorWidth );
1275 bool Engine::LayoutText( const Parameters& layoutParameters,
1276 Vector<Vector2>& glyphPositions,
1277 Vector<LineRun>& lines,
1279 bool elideTextEnabled )
1281 return mImpl->LayoutText( layoutParameters,
1288 void Engine::ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1289 CharacterIndex startIndex,
1290 Length numberOfCharacters,
1291 Vector<Vector2>& glyphPositions )
1293 mImpl->ReLayoutRightToLeftLines( layoutParameters,
1299 void Engine::Align( const Size& size,
1300 CharacterIndex startIndex,
1301 Length numberOfCharacters,
1302 Text::HorizontalAlignment::Type horizontalAlignment,
1303 Vector<LineRun>& lines,
1304 float& alignmentOffset )
1309 horizontalAlignment,
1314 void Engine::SetDefaultLineSpacing( float lineSpacing )
1316 mImpl->mDefaultLineSpacing = lineSpacing;
1319 float Engine::GetDefaultLineSpacing() const
1321 return mImpl->mDefaultLineSpacing;
1324 } // namespace Layout
1328 } // namespace Toolkit