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;
53 * @brief Stores temporary layout info of the line.
61 numberOfCharacters( 0u ),
65 wsLengthEndOfLine( 0.f ),
67 descender( MAX_FLOAT )
78 numberOfCharacters = 0u;
82 wsLengthEndOfLine = 0.f;
84 descender = MAX_FLOAT;
87 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
88 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
89 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
90 Length numberOfCharacters; ///< The number of characters which fit in one line.
91 float length; ///< The addition of the advance metric of all the glyphs which fit in one line.
92 float extraBearing; ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
93 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.
94 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
95 float ascender; ///< The maximum ascender of all fonts in the line.
96 float descender; ///< The minimum descender of all fonts in the line.
99 struct LayoutEngine::Impl
102 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
103 mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
104 mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP ),
105 mCursorWidth( CURSOR_WIDTH ),
106 mEllipsisEnabled( false )
111 * @brief Updates the line ascender and descender with the metrics of a new font.
113 * @param[in] fontId The id of the new font.
114 * @param[in,out] lineLayout The line layout.
116 void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
118 Text::FontMetrics fontMetrics;
119 mMetrics->GetFontMetrics( fontId, fontMetrics );
121 // Sets the maximum ascender.
122 if( fontMetrics.ascender > lineLayout.ascender )
124 lineLayout.ascender = fontMetrics.ascender;
127 // Sets the minimum descender.
128 if( fontMetrics.descender < lineLayout.descender )
130 lineLayout.descender = fontMetrics.descender;
135 * @brief Merges a temporary line layout into the line layout.
137 * @param[in,out] lineLayout The line layout.
138 * @param[in] tmpLineLayout A temporary line layout.
140 void MergeLineLayout( LineLayout& lineLayout,
141 const LineLayout& tmpLineLayout )
143 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
144 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
145 lineLayout.length += tmpLineLayout.length;
147 if( 0.f < tmpLineLayout.length )
149 lineLayout.length += lineLayout.wsLengthEndOfLine;
151 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
155 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
158 if( tmpLineLayout.ascender > lineLayout.ascender )
160 lineLayout.ascender = tmpLineLayout.ascender;
163 if( tmpLineLayout.descender < lineLayout.descender )
165 lineLayout.descender = tmpLineLayout.descender;
170 * Retrieves the line layout for a given box width.
172 * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
173 * because the number of characters of the line is not known (one of the responsabilities of this method
174 * is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
175 * and left to right text is laid-out, it can be small differences in the line length. One solution is to
176 * reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
177 * this method calculates which are the first and last glyphs of the line (the ones that causes the
178 * differences). This is a good point to check if there is problems with the text exceeding the boundaries
179 * of the control when there is right to left text.
181 * @param[in] parameters The layout parameters.
182 * @param[out] lineLayout The line layout.
183 * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
184 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
186 void GetLineLayoutForBox( const LayoutParameters& parameters,
187 LineLayout& lineLayout,
188 CharacterDirection& paragraphDirection,
189 bool completelyFill )
191 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
192 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
193 // Stores temporary line layout which has not been added to the final line layout.
194 LineLayout tmpLineLayout;
196 const bool isMultiline = mLayout == MULTI_LINE_BOX;
197 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
199 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
200 // In the case the line starts with a right to left character, if the width is longer than the advance,
201 // the difference needs to be added to the line length.
202 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + lineLayout.glyphIndex );
204 // Set the direction of the first character of the line.
205 lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
206 const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
207 CharacterDirection previousCharacterDirection = firstCharacterDirection;
209 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
210 float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
212 float tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
214 tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor.
216 // Calculate the line height if there is no characters.
217 FontId lastFontId = glyphInfo.fontId;
218 UpdateLineHeight( lastFontId, tmpLineLayout );
220 bool oneWordLaidOut = false;
222 const GlyphIndex lastGlyphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
223 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
224 glyphIndex < lastGlyphPlusOne;
227 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
228 const bool isLastGlyph = glyphIndex == lastGlyphIndex;
230 // Get the glyph info.
231 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
233 // Check if the font of the current glyph is the same of the previous one.
234 // If it's different the ascender and descender need to be updated.
235 if( lastFontId != glyphInfo.fontId )
237 UpdateLineHeight( glyphInfo.fontId, tmpLineLayout );
238 lastFontId = glyphInfo.fontId;
241 // Get the character indices for the current glyph. The last character index is needed
242 // because there are glyphs formed by more than one character but their break info is
243 // given only for the last character.
244 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
245 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
246 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
248 // Get the line break info for the current character.
249 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
251 // Get the word break info for the current character.
252 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
254 // Increase the number of characters.
255 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
257 // Increase the number of glyphs.
258 tmpLineLayout.numberOfGlyphs++;
260 // Check whether is a white space.
261 const Character character = *( parameters.textBuffer + characterFirstIndex );
262 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
264 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
265 const float previousTmpLineLength = tmpLineLayout.length;
266 const float previousTmpExtraBearing = tmpExtraBearing;
267 const float previousTmpExtraWidth = tmpExtraWidth;
269 // Get the character's direction.
270 const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
272 // Increase the accumulated length.
275 // Add the length to the length of white spaces at the end of the line.
276 tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // The advance is used as the width is always zero for the white spaces.
280 // Add as well any previous white space length.
281 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
283 // An extra space may be added to the line for the first and last glyph of the line.
284 // If the bearing of the first glyph is negative, its positive value needs to be added.
285 // If the bearing plus the width of the last glyph is greater than the advance, the difference
286 // needs to be added.
288 if( characterDirection == paragraphDirection )
290 if( RTL == characterDirection )
301 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
314 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
315 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
320 if( characterDirection != previousCharacterDirection )
322 if( RTL == characterDirection )
327 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
328 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
335 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
338 else if( characterDirection == firstCharacterDirection )
340 if( RTL == characterDirection )
346 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
354 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
355 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
360 // Clear the white space length at the end of the line.
361 tmpLineLayout.wsLengthEndOfLine = 0.f;
364 // Check if the accumulated length fits in the width of the box.
365 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
366 ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
368 // Current word does not fit in the box's width.
369 if( !oneWordLaidOut || completelyFill )
371 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
373 // The word doesn't fit in the control's width. It needs to be split by character.
374 if( tmpLineLayout.numberOfGlyphs > 0u )
376 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
377 --tmpLineLayout.numberOfGlyphs;
378 tmpLineLayout.length = previousTmpLineLength;
379 tmpExtraBearing = previousTmpExtraBearing;
380 tmpExtraWidth = previousTmpExtraWidth;
383 // Add part of the word to the line layout.
384 MergeLineLayout( lineLayout, tmpLineLayout );
388 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
391 lineLayout.extraBearing = tmpExtraBearing;
392 lineLayout.extraWidth = tmpExtraWidth;
394 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
399 if( ( isMultiline || isLastGlyph ) &&
400 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
402 // Must break the line. Update the line layout and return.
403 MergeLineLayout( lineLayout, tmpLineLayout );
405 // Set the next paragraph's direction.
407 ( NULL != parameters.characterDirectionBuffer ) )
409 paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
412 lineLayout.extraBearing = tmpExtraBearing;
413 lineLayout.extraWidth = tmpExtraWidth;
415 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
416 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
421 ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
423 oneWordLaidOut = true;
424 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
426 // Current glyph is the last one of the current word.
427 // Add the temporal layout to the current one.
428 MergeLineLayout( lineLayout, tmpLineLayout );
430 tmpLineLayout.Clear();
433 previousCharacterDirection = characterDirection;
436 lineLayout.extraBearing = tmpExtraBearing;
437 lineLayout.extraWidth = tmpExtraWidth;
439 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
443 * @brief Calculates the vertical offset to add to the new laid-out glyphs.
445 * @pre @p lineIndex must be between 0 and the number of lines (both inclusive).
447 * @param[in] lines The previously laid-out lines.
448 * @param[in] lineIndex Index to the line where the new laid-out lines are inserted.
450 * @return The vertical offset of the lines starting from the beginning to the line @p lineIndex.
452 float SetParagraphOffset( const Vector<LineRun>& lines,
453 LineIndex lineIndex )
457 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
458 endIt = lines.Begin() + lineIndex;
462 const LineRun& line = *it;
464 offset += line.ascender + -line.descender;
470 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
471 Length numberOfGlyphs,
472 Vector2* glyphPositionsBuffer )
474 // Traverse the glyphs and set the positions.
476 // Check if the x bearing of the first character is negative.
477 // If it has a negative x bearing, it will exceed the boundaries of the actor,
478 // so the penX position needs to be moved to the right.
480 const GlyphInfo& glyph = *glyphsBuffer;
481 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
483 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
485 const GlyphInfo& glyph = *( glyphsBuffer + i );
486 Vector2& position = *( glyphPositionsBuffer + i );
488 position.x = penX + glyph.xBearing;
489 position.y = -glyph.yBearing;
491 penX += glyph.advance;
496 * @brief Resizes the line buffer.
498 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
499 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
500 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
501 * @param[in] updateCurrentBuffer Whether the layout is updated.
503 * @return Pointer to either lines or newLines.
505 LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
506 Vector<LineRun>& newLines,
507 Length& linesCapacity,
508 bool updateCurrentBuffer )
510 LineRun* linesBuffer = NULL;
511 // Reserve more space for the next lines.
513 if( updateCurrentBuffer )
515 newLines.Resize( linesCapacity );
516 linesBuffer = newLines.Begin();
520 lines.Resize( linesCapacity );
521 linesBuffer = lines.Begin();
528 * Ellipsis a line if it exceeds the width's of the bounding box.
530 * @param[in] layoutParameters The parameters needed to layout the text.
531 * @param[in] layout The line layout.
532 * @param[in,out] layoutSize The text's layout size.
533 * @param[in,out] linesBuffer Pointer to the line's buffer.
534 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
535 * @param[in,out] numberOfLines The number of laid-out lines.
536 * @param[in] penY The vertical layout position.
537 * @param[in] currentParagraphDirection The current paragraph's direction.
539 * return Whether the line is ellipsized.
541 bool EllipsisLine( const LayoutParameters& layoutParameters,
542 const LineLayout& layout,
544 LineRun* linesBuffer,
545 Vector2* glyphPositionsBuffer,
546 Length& numberOfLines,
548 CharacterDirection currentParagraphDirection )
550 const bool ellipsis = ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
551 ( ( mLayout == SINGLE_LINE_BOX ) &&
552 ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) );
556 // Do not layout more lines if ellipsis is enabled.
558 // The last line needs to be completely filled with characters.
559 // Part of a word may be used.
561 LineRun* lineRun = NULL;
562 LineLayout ellipsisLayout;
563 if( 0u != numberOfLines )
565 // Get the last line and layout it again with the 'completelyFill' flag to true.
566 lineRun = linesBuffer + ( numberOfLines - 1u );
568 penY -= layout.ascender - lineRun->descender;
570 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
574 // At least there is space reserved for one line.
575 lineRun = linesBuffer;
577 lineRun->glyphRun.glyphIndex = 0u;
578 ellipsisLayout.glyphIndex = 0u;
583 GetLineLayoutForBox( layoutParameters,
585 currentParagraphDirection,
588 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
589 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
590 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
591 lineRun->width = ellipsisLayout.length;
592 lineRun->extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
593 lineRun->ascender = ellipsisLayout.ascender;
594 lineRun->descender = ellipsisLayout.descender;
595 lineRun->direction = !RTL;
596 lineRun->ellipsis = true;
598 layoutSize.width = layoutParameters.boundingBox.width;
599 layoutSize.height += ( lineRun->ascender + -lineRun->descender );
601 SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
602 ellipsisLayout.numberOfGlyphs,
603 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
610 * @brief Updates the text layout with a new laid-out line.
612 * @param[in] layoutParameters The parameters needed to layout the text.
613 * @param[in] layout The line layout.
614 * @param[in,out] layoutSize The text's layout size.
615 * @param[in,out] linesBuffer Pointer to the line's buffer.
616 * @param[in] index Index to the vector of glyphs.
617 * @param[in,out] numberOfLines The number of laid-out lines.
618 * @param[in] isLastLine Whether the laid-out line is the last one.
620 void UpdateTextLayout( const LayoutParameters& layoutParameters,
621 const LineLayout& layout,
623 LineRun* linesBuffer,
625 Length& numberOfLines,
628 LineRun& lineRun = *( linesBuffer + numberOfLines );
631 lineRun.glyphRun.glyphIndex = index;
632 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
633 lineRun.characterRun.characterIndex = layout.characterIndex;
634 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
635 if( isLastLine && !layoutParameters.isLastNewParagraph )
637 const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
638 if( MULTI_LINE_BOX == mLayout )
640 lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
644 lineRun.width = width;
647 lineRun.extraLength = 0.f;
651 lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
652 lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
654 lineRun.ascender = layout.ascender;
655 lineRun.descender = layout.descender;
656 lineRun.direction = !RTL;
657 lineRun.ellipsis = false;
659 // Update the actual size.
660 if( lineRun.width > layoutSize.width )
662 layoutSize.width = lineRun.width;
665 layoutSize.height += ( lineRun.ascender + -lineRun.descender );
669 * @brief Updates the text layout with the last laid-out line.
671 * @param[in] layoutParameters The parameters needed to layout the text.
672 * @param[in] characterIndex The character index of the line.
673 * @param[in] glyphIndex The glyph index of the line.
674 * @param[in,out] layoutSize The text's layout size.
675 * @param[in,out] linesBuffer Pointer to the line's buffer.
676 * @param[in,out] numberOfLines The number of laid-out lines.
678 void UpdateTextLayout( const LayoutParameters& layoutParameters,
679 CharacterIndex characterIndex,
680 GlyphIndex glyphIndex,
682 LineRun* linesBuffer,
683 Length& numberOfLines )
685 // Need to add a new line with no characters but with height to increase the layoutSize.height
686 const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
688 Text::FontMetrics fontMetrics;
689 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
691 LineRun& lineRun = *( linesBuffer + numberOfLines );
694 lineRun.glyphRun.glyphIndex = glyphIndex;
695 lineRun.glyphRun.numberOfGlyphs = 0u;
696 lineRun.characterRun.characterIndex = characterIndex;
697 lineRun.characterRun.numberOfCharacters = 0u;
699 lineRun.ascender = fontMetrics.ascender;
700 lineRun.descender = fontMetrics.descender;
701 lineRun.extraLength = 0.f;
702 lineRun.alignmentOffset = 0.f;
703 lineRun.direction = !RTL;
704 lineRun.ellipsis = false;
706 layoutSize.height += ( lineRun.ascender + -lineRun.descender );
710 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
712 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
713 * @param[in,out] layoutSize The text's layout size.
715 void UpdateLayoutSize( const Vector<LineRun>& lines,
718 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
723 const LineRun& line = *it;
725 if( line.width > layoutSize.width )
727 layoutSize.width = line.width;
730 layoutSize.height += ( line.ascender + -line.descender );
735 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
737 * @param[in] layoutParameters The parameters needed to layout the text.
738 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
739 * @param[in] characterOffset The offset to be added to the runs of characters.
740 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
742 void UpdateLineIndexOffsets( const LayoutParameters& layoutParameters,
743 Vector<LineRun>& lines,
744 Length characterOffset,
747 // Update the glyph and character runs.
748 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
755 line.glyphRun.glyphIndex = glyphOffset;
756 line.characterRun.characterIndex = characterOffset;
758 glyphOffset += line.glyphRun.numberOfGlyphs;
759 characterOffset += line.characterRun.numberOfCharacters;
763 bool LayoutText( const LayoutParameters& layoutParameters,
764 Vector<Vector2>& glyphPositions,
765 Vector<LineRun>& lines,
768 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
769 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
771 if( 0u == layoutParameters.numberOfGlyphs )
773 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
774 if( layoutParameters.isLastNewParagraph )
776 Length numberOfLines = lines.Count();
777 if( 0u != numberOfLines )
779 const LineRun& lastLine = *( lines.End() - 1u );
781 if( 0u != lastLine.characterRun.numberOfCharacters )
783 // Need to add a new line with no characters but with height to increase the layoutSize.height
785 lines.PushBack( newLine );
787 UpdateTextLayout( layoutParameters,
788 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
789 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
797 // Nothing else do if there are no glyphs to layout.
801 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
803 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
804 // This extra line needs to be removed.
805 if( 0u != lines.Count() )
807 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
809 if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
810 ( lastGlyphPlusOne == layoutParameters.totalNumberOfGlyphs ) )
812 lines.Remove( lastLine );
816 // Set the first paragraph's direction.
817 CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
819 // Whether the layout is being updated or set from scratch.
820 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
822 Vector2* glyphPositionsBuffer = NULL;
823 Vector<Vector2> newGlyphPositions;
825 LineRun* linesBuffer = NULL;
826 Vector<LineRun> newLines;
828 // Estimate the number of lines.
829 Length linesCapacity = layoutParameters.estimatedNumberOfLines;
830 Length numberOfLines = 0u;
832 if( updateCurrentBuffer )
834 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
835 glyphPositionsBuffer = newGlyphPositions.Begin();
837 newLines.Resize( linesCapacity );
838 linesBuffer = newLines.Begin();
842 glyphPositionsBuffer = glyphPositions.Begin();
844 lines.Resize( linesCapacity );
845 linesBuffer = lines.Begin();
848 float penY = SetParagraphOffset( lines,
849 layoutParameters.startLineIndex );
851 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
853 CharacterDirection currentParagraphDirection = paragraphDirection;
855 // Get the layout for the line.
857 layout.glyphIndex = index;
858 GetLineLayoutForBox( layoutParameters,
863 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
864 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
865 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
866 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
867 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
869 if( 0u == layout.numberOfGlyphs )
871 // The width is too small and no characters are laid-out.
872 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
874 lines.Resize( numberOfLines );
878 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
880 penY += layout.ascender;
882 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
884 bool ellipsis = false;
885 if( mEllipsisEnabled )
887 // Does the ellipsis of the last line.
888 ellipsis = EllipsisLine( layoutParameters,
892 glyphPositionsBuffer,
895 currentParagraphDirection );
900 // No more lines to layout.
905 // Whether the last line has been laid-out.
906 const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
908 if( numberOfLines == linesCapacity )
910 // Reserve more space for the next lines.
911 linesBuffer = ResizeLinesBuffer( lines,
914 updateCurrentBuffer );
917 // Updates the current text's layout with the line's layout.
918 UpdateTextLayout( layoutParameters,
926 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
928 if( ( nextIndex == layoutParameters.totalNumberOfGlyphs ) &&
929 layoutParameters.isLastNewParagraph &&
930 ( mLayout == MULTI_LINE_BOX ) )
932 // The last character of the text is a new paragraph character.
933 // An extra line with no characters is added to increase the text's height
934 // in order to place the cursor.
936 if( numberOfLines == linesCapacity )
938 // Reserve more space for the next lines.
939 linesBuffer = ResizeLinesBuffer( lines,
942 updateCurrentBuffer );
945 UpdateTextLayout( layoutParameters,
946 layout.characterIndex + layout.numberOfCharacters,
947 index + layout.numberOfGlyphs,
951 } // whether to add a last line.
953 // Sets the positions of the glyphs.
954 SetGlyphPositions( layoutParameters.glyphsBuffer + index,
955 layout.numberOfGlyphs,
956 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
958 // Updates the vertical pen's position.
959 penY += -layout.descender;
961 // Increase the glyph index.
964 } // end for() traversing glyphs.
966 if( updateCurrentBuffer )
968 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
969 newGlyphPositions.Begin(),
970 newGlyphPositions.End() );
971 glyphPositions.Resize( layoutParameters.totalNumberOfGlyphs );
973 newLines.Resize( numberOfLines );
975 // Current text's layout size adds only the newly laid-out lines.
976 // Updates the layout size with the previously laid-out lines.
977 UpdateLayoutSize( lines,
980 if( 0u != newLines.Count() )
982 const LineRun& lastLine = *( newLines.End() - 1u );
984 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
985 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
987 // Update the indices of the runs before the new laid-out lines are inserted.
988 UpdateLineIndexOffsets( layoutParameters,
994 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1001 lines.Resize( numberOfLines );
1004 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1009 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
1010 CharacterIndex startIndex,
1011 Length numberOfCharacters,
1012 Vector<Vector2>& glyphPositions )
1014 const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
1016 // Traverses the paragraphs with right to left characters.
1017 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
1019 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
1021 if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
1023 // Do not reorder the line if it has been already reordered.
1027 if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
1029 // Do not reorder the lines after the last requested character.
1033 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
1034 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
1036 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
1038 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
1040 // Traverses the characters of the right to left paragraph.
1041 for( CharacterIndex characterLogicalIndex = 0u;
1042 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1043 ++characterLogicalIndex )
1045 // Convert the character in the logical order into the character in the visual order.
1046 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
1048 // Get the number of glyphs of the character.
1049 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
1051 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1053 // Convert the character in the visual order into the glyph in the visual order.
1054 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
1056 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
1058 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
1059 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1061 position.x = penX + glyph.xBearing;
1062 penX += glyph.advance;
1068 void Align( const Size& size,
1069 CharacterIndex startIndex,
1070 Length numberOfCharacters,
1071 Vector<LineRun>& lines )
1073 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1075 // Traverse all lines and align the glyphs.
1076 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1080 LineRun& line = *it;
1082 if( line.characterRun.characterIndex < startIndex )
1084 // Do not align lines which have already been aligned.
1088 if( line.characterRun.characterIndex >= lastCharacterPlusOne )
1090 // Do not align lines beyond the last laid-out character.
1094 // Calculate the line's alignment offset accordingly with the align option,
1095 // the box width, line length, and the paragraph's direction.
1096 CalculateHorizontalAlignment( size.width,
1101 void CalculateHorizontalAlignment( float boxWidth,
1104 line.alignmentOffset = 0.f;
1105 const bool isRTL = RTL == line.direction;
1106 float lineLength = line.width;
1108 HorizontalAlignment alignment = mHorizontalAlignment;
1111 // Swap the alignment type if the line is right to left.
1114 case HORIZONTAL_ALIGN_BEGIN:
1116 alignment = HORIZONTAL_ALIGN_END;
1119 case HORIZONTAL_ALIGN_CENTER:
1124 case HORIZONTAL_ALIGN_END:
1126 alignment = HORIZONTAL_ALIGN_BEGIN;
1132 // Calculate the horizontal line offset.
1135 case HORIZONTAL_ALIGN_BEGIN:
1137 line.alignmentOffset = 0.f;
1141 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1142 line.alignmentOffset -= line.extraLength;
1146 case HORIZONTAL_ALIGN_CENTER:
1148 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1152 line.alignmentOffset -= line.extraLength;
1155 line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
1158 case HORIZONTAL_ALIGN_END:
1162 lineLength += line.extraLength;
1165 line.alignmentOffset = boxWidth - lineLength;
1171 LayoutEngine::Layout mLayout;
1172 LayoutEngine::HorizontalAlignment mHorizontalAlignment;
1173 LayoutEngine::VerticalAlignment mVerticalAlignment;
1176 IntrusivePtr<Metrics> mMetrics;
1178 bool mEllipsisEnabled:1;
1181 LayoutEngine::LayoutEngine()
1184 mImpl = new LayoutEngine::Impl();
1187 LayoutEngine::~LayoutEngine()
1192 void LayoutEngine::SetMetrics( MetricsPtr& metrics )
1194 mImpl->mMetrics = metrics;
1197 void LayoutEngine::SetLayout( Layout layout )
1199 mImpl->mLayout = layout;
1202 LayoutEngine::Layout LayoutEngine::GetLayout() const
1204 return mImpl->mLayout;
1207 void LayoutEngine::SetTextEllipsisEnabled( bool enabled )
1209 mImpl->mEllipsisEnabled = enabled;
1212 bool LayoutEngine::GetTextEllipsisEnabled() const
1214 return mImpl->mEllipsisEnabled;
1217 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
1219 mImpl->mHorizontalAlignment = alignment;
1222 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
1224 return mImpl->mHorizontalAlignment;
1227 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
1229 mImpl->mVerticalAlignment = alignment;
1232 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
1234 return mImpl->mVerticalAlignment;
1237 void LayoutEngine::SetCursorWidth( int width )
1239 mImpl->mCursorWidth = static_cast<float>( width );
1242 int LayoutEngine::GetCursorWidth() const
1244 return static_cast<int>( mImpl->mCursorWidth );
1247 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
1248 Vector<Vector2>& glyphPositions,
1249 Vector<LineRun>& lines,
1252 return mImpl->LayoutText( layoutParameters,
1258 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
1259 CharacterIndex startIndex,
1260 Length numberOfCharacters,
1261 Vector<Vector2>& glyphPositions )
1263 mImpl->ReLayoutRightToLeftLines( layoutParameters,
1269 void LayoutEngine::Align( const Size& size,
1270 CharacterIndex startIndex,
1271 Length numberOfCharacters,
1272 Vector<LineRun>& lines )
1282 } // namespace Toolkit