2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/devel-api/text-abstraction/font-client.h>
27 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
28 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
29 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
30 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
51 const float MAX_FLOAT = std::numeric_limits<float>::max();
52 const bool RTL = true;
53 const float LINE_SPACING= 0.f;
58 * @brief Stores temporary layout info of the line.
66 numberOfCharacters( 0u ),
67 ascender( -MAX_FLOAT ),
68 descender( MAX_FLOAT ),
71 previousAdvance( 0.f ),
73 wsLengthEndOfLine( 0.f )
84 numberOfCharacters = 0u;
86 descender = MAX_FLOAT;
89 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
90 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
91 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
92 Length numberOfCharacters; ///< The number of characters which fit in one line.
93 float ascender; ///< The maximum ascender of all fonts in the line.
94 float descender; ///< The minimum descender of all fonts in the line.
95 float lineSpacing; ///< The line spacing
96 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
97 float previousAdvance; ///< The advance of the previous glyph.
98 float length; ///< The current length of the line.
99 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
105 : mLayout( Layout::Engine::SINGLE_LINE_BOX ),
107 mDefaultLineSpacing( LINE_SPACING )
112 * @brief Updates the line ascender and descender with the metrics of a new font.
114 * @param[in] glyphMetrics The metrics of the new font.
115 * @param[in,out] lineLayout The line layout.
117 void UpdateLineHeight( const GlyphMetrics& glyphMetrics, LineLayout& lineLayout )
119 Text::FontMetrics fontMetrics;
120 if( 0u != glyphMetrics.fontId )
122 mMetrics->GetFontMetrics( glyphMetrics.fontId, fontMetrics );
126 fontMetrics.ascender = glyphMetrics.fontHeight;
127 fontMetrics.descender = 0.f;
128 fontMetrics.height = fontMetrics.ascender;
129 fontMetrics.underlinePosition = 0.f;
130 fontMetrics.underlineThickness = 1.f;
133 // Sets the maximum ascender.
134 lineLayout.ascender = std::max( lineLayout.ascender, fontMetrics.ascender );
136 // Sets the minimum descender.
137 lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender );
139 // set the line spacing
140 lineLayout.lineSpacing = mDefaultLineSpacing;
144 * @brief Merges a temporary line layout into the line layout.
146 * @param[in,out] lineLayout The line layout.
147 * @param[in] tmpLineLayout A temporary line layout.
149 void MergeLineLayout( LineLayout& lineLayout,
150 const LineLayout& tmpLineLayout )
152 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
153 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
155 lineLayout.penX = tmpLineLayout.penX;
156 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
158 lineLayout.length = tmpLineLayout.length;
159 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
161 // Sets the maximum ascender.
162 lineLayout.ascender = std::max( lineLayout.ascender, tmpLineLayout.ascender );
164 // Sets the minimum descender.
165 lineLayout.descender = std::min( lineLayout.descender, tmpLineLayout.descender );
169 * Retrieves the line layout for a given box width.
171 * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
172 * because the number of characters of the line is not known (one of the responsabilities of this method
173 * is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
174 * and left to right text is laid-out, it can be small differences in the line length. One solution is to
175 * reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
176 * this method calculates which are the first and last glyphs of the line (the ones that causes the
177 * differences). This is a good point to check if there is problems with the text exceeding the boundaries
178 * of the control when there is right to left text.
180 * @param[in] parameters The layout parameters.
181 * @param[out] lineLayout The line layout.
182 * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
183 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
185 void GetLineLayoutForBox( const Parameters& parameters,
186 LineLayout& lineLayout,
187 CharacterDirection& paragraphDirection,
188 bool completelyFill )
190 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
191 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex );
193 const bool isMultiline = mLayout == MULTI_LINE_BOX;
194 const bool isWordLaidOut = parameters.lineWrapMode == Text::LineWrap::WORD;
196 // The last glyph to be laid-out.
197 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
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.
203 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
204 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
205 lastGlyphOfParagraphPlusOne,
206 parameters.charactersPerGlyphBuffer );
208 GlyphMetrics glyphMetrics;
209 GetGlyphsMetrics( lineLayout.glyphIndex,
210 numberOfGLyphsInGroup,
212 parameters.glyphsBuffer,
215 // Set the direction of the first character of the line.
216 lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
218 // Stores temporary line layout which has not been added to the final line layout.
219 LineLayout tmpLineLayout;
221 // Initialize the start point.
223 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
224 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
225 // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined.
226 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + parameters.outlineWidth;
228 // Initialize the advance of the previous glyph.
229 tmpLineLayout.previousAdvance = 0.f;
231 // Calculate the line height if there is no characters.
232 FontId lastFontId = glyphMetrics.fontId;
233 UpdateLineHeight( glyphMetrics, tmpLineLayout );
235 bool oneWordLaidOut = false;
237 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
238 glyphIndex < lastGlyphOfParagraphPlusOne; )
240 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
242 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
243 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
244 lastGlyphOfParagraphPlusOne,
245 parameters.charactersPerGlyphBuffer );
247 GlyphMetrics glyphMetrics;
248 GetGlyphsMetrics( glyphIndex,
249 numberOfGLyphsInGroup,
251 parameters.glyphsBuffer,
254 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == parameters.totalNumberOfGlyphs;
256 // Check if the font of the current glyph is the same of the previous one.
257 // If it's different the ascender and descender need to be updated.
258 if( lastFontId != glyphMetrics.fontId )
260 UpdateLineHeight( glyphMetrics, tmpLineLayout );
261 lastFontId = glyphMetrics.fontId;
264 // Get the character indices for the current glyph. The last character index is needed
265 // because there are glyphs formed by more than one character but their break info is
266 // given only for the last character.
267 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
268 const bool hasCharacters = charactersPerGlyph > 0u;
269 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
270 const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
272 // Get the line break info for the current character.
273 const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
275 // Increase the number of characters.
276 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
278 // Increase the number of glyphs.
279 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
281 // Check whether is a white space.
282 const Character character = *( parameters.textBuffer + characterFirstIndex );
283 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
285 // Calculate the length of the line.
287 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
288 const float previousTmpPenX = tmpLineLayout.penX;
289 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
290 const float previousTmpLength = tmpLineLayout.length;
291 const float previousTmpWsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
295 // Add the length to the length of white spaces at the end of the line.
296 tmpLineLayout.wsLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
300 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.wsLengthEndOfLine;
301 tmpLineLayout.previousAdvance = ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
302 tmpLineLayout.length = tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width;
304 // Clear the white space length at the end of the line.
305 tmpLineLayout.wsLengthEndOfLine = 0.f;
308 // Check if the accumulated length fits in the width of the box.
309 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
310 ( tmpLineLayout.length > parameters.boundingBox.width ) )
312 // Current word does not fit in the box's width.
313 if( !oneWordLaidOut || completelyFill )
315 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
317 // The word doesn't fit in the control's width. It needs to be split by character.
318 if( tmpLineLayout.numberOfGlyphs > 0u )
320 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
321 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
323 tmpLineLayout.penX = previousTmpPenX;
324 tmpLineLayout.previousAdvance = previousTmpAdvance;
325 tmpLineLayout.length = previousTmpLength;
326 tmpLineLayout.wsLengthEndOfLine = previousTmpWsLengthEndOfLine;
329 // Add part of the word to the line layout.
330 MergeLineLayout( lineLayout, tmpLineLayout );
334 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
337 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
342 if( ( isMultiline || isLastGlyph ) &&
343 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
345 // Must break the line. Update the line layout and return.
346 MergeLineLayout( lineLayout, tmpLineLayout );
348 // Set the next paragraph's direction.
350 ( NULL != parameters.characterDirectionBuffer ) )
352 paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
355 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
356 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
362 ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
364 oneWordLaidOut = isWordLaidOut;
365 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
367 // Current glyph is the last one of the current word.
368 // Add the temporal layout to the current one.
369 MergeLineLayout( lineLayout, tmpLineLayout );
371 tmpLineLayout.Clear();
374 glyphIndex += numberOfGLyphsInGroup;
377 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
380 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
381 Length numberOfGlyphs,
383 float interGlyphExtraAdvance,
384 Vector2* glyphPositionsBuffer )
386 // Traverse the glyphs and set the positions.
388 // Check if the x bearing of the first character is negative.
389 // If it has a negative x bearing, it will exceed the boundaries of the actor,
390 // so the penX position needs to be moved to the right.
392 const GlyphInfo& glyph = *glyphsBuffer;
393 float penX = -glyph.xBearing + mCursorWidth + outlineWidth;
395 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
397 const GlyphInfo& glyph = *( glyphsBuffer + i );
398 Vector2& position = *( glyphPositionsBuffer + i );
400 position.x = std::round( penX + glyph.xBearing );
401 position.y = -glyph.yBearing;
403 penX += ( glyph.advance + interGlyphExtraAdvance );
408 * @brief Resizes the line buffer.
410 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
411 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
412 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
413 * @param[in] updateCurrentBuffer Whether the layout is updated.
415 * @return Pointer to either lines or newLines.
417 LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
418 Vector<LineRun>& newLines,
419 Length& linesCapacity,
420 bool updateCurrentBuffer )
422 LineRun* linesBuffer = NULL;
423 // Reserve more space for the next lines.
425 if( updateCurrentBuffer )
427 newLines.Resize( linesCapacity );
428 linesBuffer = newLines.Begin();
432 lines.Resize( linesCapacity );
433 linesBuffer = lines.Begin();
440 * Ellipsis a line if it exceeds the width's of the bounding box.
442 * @param[in] layoutParameters The parameters needed to layout the text.
443 * @param[in] layout The line layout.
444 * @param[in,out] layoutSize The text's layout size.
445 * @param[in,out] linesBuffer Pointer to the line's buffer.
446 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
447 * @param[in,out] numberOfLines The number of laid-out lines.
448 * @param[in] penY The vertical layout position.
449 * @param[in] currentParagraphDirection The current paragraph's direction.
450 * @param[in,out] isAutoScrollEnabled If the isAutoScrollEnabled is true and the height of the text exceeds the boundaries of the control the text is elided and the isAutoScrollEnabled is set to false to disable the autoscroll
452 * return Whether the line is ellipsized.
454 bool EllipsisLine( const Parameters& layoutParameters,
455 const LineLayout& layout,
457 LineRun* linesBuffer,
458 Vector2* glyphPositionsBuffer,
459 Length& numberOfLines,
461 CharacterDirection currentParagraphDirection,
462 bool& isAutoScrollEnabled )
464 const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) :
465 ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
466 ( ( mLayout == SINGLE_LINE_BOX ) &&
467 ( layout.length > layoutParameters.boundingBox.width ) ) );
471 isAutoScrollEnabled = false;
472 // Do not layout more lines if ellipsis is enabled.
474 // The last line needs to be completely filled with characters.
475 // Part of a word may be used.
477 LineRun* lineRun = NULL;
478 LineLayout ellipsisLayout;
479 if( 0u != numberOfLines )
481 // Get the last line and layout it again with the 'completelyFill' flag to true.
482 lineRun = linesBuffer + ( numberOfLines - 1u );
484 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
486 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
490 // At least there is space reserved for one line.
491 lineRun = linesBuffer;
493 lineRun->glyphRun.glyphIndex = 0u;
494 ellipsisLayout.glyphIndex = 0u;
499 GetLineLayoutForBox( layoutParameters,
501 currentParagraphDirection,
504 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
505 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
506 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
507 lineRun->width = ellipsisLayout.length;
508 lineRun->extraLength = std::ceil( ellipsisLayout.wsLengthEndOfLine );
509 lineRun->ascender = ellipsisLayout.ascender;
510 lineRun->descender = ellipsisLayout.descender;
511 lineRun->direction = !RTL;
512 lineRun->ellipsis = true;
514 layoutSize.width = layoutParameters.boundingBox.width;
515 if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
517 layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
520 SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
521 ellipsisLayout.numberOfGlyphs,
522 layoutParameters.outlineWidth,
523 layoutParameters.interGlyphExtraAdvance,
524 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
531 * @brief Updates the text layout with a new laid-out line.
533 * @param[in] layoutParameters The parameters needed to layout the text.
534 * @param[in] layout The line layout.
535 * @param[in,out] layoutSize The text's layout size.
536 * @param[in,out] linesBuffer Pointer to the line's buffer.
537 * @param[in] index Index to the vector of glyphs.
538 * @param[in,out] numberOfLines The number of laid-out lines.
539 * @param[in] isLastLine Whether the laid-out line is the last one.
541 void UpdateTextLayout( const Parameters& layoutParameters,
542 const LineLayout& layout,
544 LineRun* linesBuffer,
546 Length& numberOfLines,
549 LineRun& lineRun = *( linesBuffer + numberOfLines );
552 lineRun.glyphRun.glyphIndex = index;
553 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
554 lineRun.characterRun.characterIndex = layout.characterIndex;
555 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
556 lineRun.lineSpacing = mDefaultLineSpacing;
558 if( isLastLine && !layoutParameters.isLastNewParagraph )
560 const float width = layout.length + layout.wsLengthEndOfLine;
561 if( MULTI_LINE_BOX == mLayout )
563 lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
567 lineRun.width = width;
570 lineRun.extraLength = 0.f;
574 lineRun.width = layout.length;
575 lineRun.extraLength = std::ceil( layout.wsLengthEndOfLine );
578 // Rounds upward to avoid a non integer size.
579 lineRun.width = std::ceil( lineRun.width );
581 lineRun.ascender = layout.ascender;
582 lineRun.descender = layout.descender;
583 lineRun.direction = !RTL;
584 lineRun.ellipsis = false;
586 // Update the actual size.
587 if( lineRun.width > layoutSize.width )
589 layoutSize.width = lineRun.width;
592 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
596 * @brief Updates the text layout with the last laid-out line.
598 * @param[in] layoutParameters The parameters needed to layout the text.
599 * @param[in] characterIndex The character index of the line.
600 * @param[in] glyphIndex The glyph index of the line.
601 * @param[in,out] layoutSize The text's layout size.
602 * @param[in,out] linesBuffer Pointer to the line's buffer.
603 * @param[in,out] numberOfLines The number of laid-out lines.
605 void UpdateTextLayout( const Parameters& layoutParameters,
606 CharacterIndex characterIndex,
607 GlyphIndex glyphIndex,
609 LineRun* linesBuffer,
610 Length& numberOfLines )
612 // Need to add a new line with no characters but with height to increase the layoutSize.height
613 const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
615 Text::FontMetrics fontMetrics;
616 if( 0u != glyphInfo.fontId )
618 mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
621 LineRun& lineRun = *( linesBuffer + numberOfLines );
624 lineRun.glyphRun.glyphIndex = glyphIndex;
625 lineRun.glyphRun.numberOfGlyphs = 0u;
626 lineRun.characterRun.characterIndex = characterIndex;
627 lineRun.characterRun.numberOfCharacters = 0u;
629 lineRun.ascender = fontMetrics.ascender;
630 lineRun.descender = fontMetrics.descender;
631 lineRun.extraLength = 0.f;
632 lineRun.alignmentOffset = 0.f;
633 lineRun.direction = !RTL;
634 lineRun.ellipsis = false;
635 lineRun.lineSpacing = mDefaultLineSpacing;
637 layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
641 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
643 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
644 * @param[in,out] layoutSize The text's layout size.
646 void UpdateLayoutSize( const Vector<LineRun>& lines,
649 for( Vector<LineRun>::ConstIterator it = lines.Begin(),
654 const LineRun& line = *it;
656 if( line.width > layoutSize.width )
658 layoutSize.width = line.width;
661 layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
666 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
668 * @param[in] layoutParameters The parameters needed to layout the text.
669 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
670 * @param[in] characterOffset The offset to be added to the runs of characters.
671 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
673 void UpdateLineIndexOffsets( const Parameters& layoutParameters,
674 Vector<LineRun>& lines,
675 Length characterOffset,
678 // Update the glyph and character runs.
679 for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
686 line.glyphRun.glyphIndex = glyphOffset;
687 line.characterRun.characterIndex = characterOffset;
689 glyphOffset += line.glyphRun.numberOfGlyphs;
690 characterOffset += line.characterRun.numberOfCharacters;
694 bool LayoutText( const Parameters& layoutParameters,
695 Vector<Vector2>& glyphPositions,
696 Vector<LineRun>& lines,
698 bool elideTextEnabled,
699 bool& isAutoScrollEnabled )
701 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
702 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
704 if( 0u == layoutParameters.numberOfGlyphs )
706 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
707 if( layoutParameters.isLastNewParagraph )
709 Length numberOfLines = lines.Count();
710 if( 0u != numberOfLines )
712 const LineRun& lastLine = *( lines.End() - 1u );
714 if( 0u != lastLine.characterRun.numberOfCharacters )
716 // Need to add a new line with no characters but with height to increase the layoutSize.height
718 Initialize( newLine );
719 lines.PushBack( newLine );
721 UpdateTextLayout( layoutParameters,
722 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
723 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
731 // Calculates the layout size.
732 UpdateLayoutSize( lines,
735 // Rounds upward to avoid a non integer size.
736 layoutSize.height = std::ceil( layoutSize.height );
738 // Nothing else do if there are no glyphs to layout.
742 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
744 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
745 // This extra line needs to be removed.
746 if( 0u != lines.Count() )
748 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
750 if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
751 ( lastGlyphPlusOne == layoutParameters.totalNumberOfGlyphs ) )
753 lines.Remove( lastLine );
757 // Set the first paragraph's direction.
758 CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
760 // Whether the layout is being updated or set from scratch.
761 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
763 Vector2* glyphPositionsBuffer = NULL;
764 Vector<Vector2> newGlyphPositions;
766 LineRun* linesBuffer = NULL;
767 Vector<LineRun> newLines;
769 // Estimate the number of lines.
770 Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
771 Length numberOfLines = 0u;
773 if( updateCurrentBuffer )
775 newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
776 glyphPositionsBuffer = newGlyphPositions.Begin();
778 newLines.Resize( linesCapacity );
779 linesBuffer = newLines.Begin();
783 glyphPositionsBuffer = glyphPositions.Begin();
785 lines.Resize( linesCapacity );
786 linesBuffer = lines.Begin();
789 float penY = CalculateLineOffset( lines,
790 layoutParameters.startLineIndex );
792 for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
794 CharacterDirection currentParagraphDirection = paragraphDirection;
796 // Get the layout for the line.
798 layout.glyphIndex = index;
799 GetLineLayoutForBox( layoutParameters,
804 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
805 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
806 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
807 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
808 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
810 if( 0u == layout.numberOfGlyphs )
812 // The width is too small and no characters are laid-out.
813 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
815 lines.Resize( numberOfLines );
817 // Rounds upward to avoid a non integer size.
818 layoutSize.height = std::ceil( layoutSize.height );
823 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
825 penY += layout.ascender;
827 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
829 bool ellipsis = false;
830 if( elideTextEnabled )
832 // Does the ellipsis of the last line.
833 ellipsis = EllipsisLine( layoutParameters,
837 glyphPositionsBuffer,
840 currentParagraphDirection,
841 isAutoScrollEnabled );
846 // No more lines to layout.
851 // Whether the last line has been laid-out.
852 const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
854 if( numberOfLines == linesCapacity )
856 // Reserve more space for the next lines.
857 linesBuffer = ResizeLinesBuffer( lines,
860 updateCurrentBuffer );
863 // Updates the current text's layout with the line's layout.
864 UpdateTextLayout( layoutParameters,
872 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
874 if( ( nextIndex == layoutParameters.totalNumberOfGlyphs ) &&
875 layoutParameters.isLastNewParagraph &&
876 ( mLayout == MULTI_LINE_BOX ) )
878 // The last character of the text is a new paragraph character.
879 // An extra line with no characters is added to increase the text's height
880 // in order to place the cursor.
882 if( numberOfLines == linesCapacity )
884 // Reserve more space for the next lines.
885 linesBuffer = ResizeLinesBuffer( lines,
888 updateCurrentBuffer );
891 UpdateTextLayout( layoutParameters,
892 layout.characterIndex + layout.numberOfCharacters,
893 index + layout.numberOfGlyphs,
897 } // whether to add a last line.
899 // Sets the positions of the glyphs.
900 SetGlyphPositions( layoutParameters.glyphsBuffer + index,
901 layout.numberOfGlyphs,
902 layoutParameters.outlineWidth,
903 layoutParameters.interGlyphExtraAdvance,
904 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
906 // Updates the vertical pen's position.
907 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
909 // Increase the glyph index.
912 } // end for() traversing glyphs.
914 if( updateCurrentBuffer )
916 glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
917 newGlyphPositions.Begin(),
918 newGlyphPositions.End() );
919 glyphPositions.Resize( layoutParameters.totalNumberOfGlyphs );
921 newLines.Resize( numberOfLines );
923 // Current text's layout size adds only the newly laid-out lines.
924 // Updates the layout size with the previously laid-out lines.
925 UpdateLayoutSize( lines,
928 if( 0u != newLines.Count() )
930 const LineRun& lastLine = *( newLines.End() - 1u );
932 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
933 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
935 // Update the indices of the runs before the new laid-out lines are inserted.
936 UpdateLineIndexOffsets( layoutParameters,
942 lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
949 lines.Resize( numberOfLines );
952 // Rounds upward to avoid a non integer size.
953 layoutSize.height = std::ceil( layoutSize.height );
955 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
960 void ReLayoutRightToLeftLines( const Parameters& layoutParameters,
961 CharacterIndex startIndex,
962 Length numberOfCharacters,
963 Vector<Vector2>& glyphPositions )
965 const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
967 // Traverses the paragraphs with right to left characters.
968 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
970 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
972 if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
974 // Do not reorder the line if it has been already reordered.
978 if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
980 // Do not reorder the lines after the last requested character.
984 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
985 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
987 float penX = -glyph.xBearing + layoutParameters.outlineWidth + mCursorWidth;
989 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
991 // Traverses the characters of the right to left paragraph.
992 for( CharacterIndex characterLogicalIndex = 0u;
993 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
994 ++characterLogicalIndex )
996 // Convert the character in the logical order into the character in the visual order.
997 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
999 // Get the number of glyphs of the character.
1000 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
1002 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1004 // Convert the character in the visual order into the glyph in the visual order.
1005 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
1007 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
1009 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
1010 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1012 position.x = std::round( penX + glyph.xBearing );
1013 penX += ( glyph.advance + layoutParameters.interGlyphExtraAdvance );
1019 void Align( const Size& size,
1020 CharacterIndex startIndex,
1021 Length numberOfCharacters,
1022 Text::HorizontalAlignment::Type horizontalAlignment,
1023 Vector<LineRun>& lines,
1024 float& alignmentOffset,
1025 Dali::LayoutDirection::Type layoutDirection,
1026 bool matchSystemLanguageDirection )
1028 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1030 alignmentOffset = MAX_FLOAT;
1031 // Traverse all lines and align the glyphs.
1032 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1036 LineRun& line = *it;
1038 if( line.characterRun.characterIndex < startIndex )
1040 // Do not align lines which have already been aligned.
1044 if( line.characterRun.characterIndex >= lastCharacterPlusOne )
1046 // Do not align lines beyond the last laid-out character.
1050 // Calculate the line's alignment offset accordingly with the align option,
1051 // the box width, line length, and the paragraph's direction.
1052 CalculateHorizontalAlignment( size.width,
1053 horizontalAlignment,
1056 matchSystemLanguageDirection );
1058 // Updates the alignment offset.
1059 alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1063 void CalculateHorizontalAlignment( float boxWidth,
1064 HorizontalAlignment::Type horizontalAlignment,
1066 Dali::LayoutDirection::Type layoutDirection,
1067 bool matchSystemLanguageDirection )
1069 line.alignmentOffset = 0.f;
1070 const bool isLineRTL = RTL == line.direction;
1071 // Whether to swap the alignment.
1072 // Swap if the line is RTL and is not required to match the direction of the system's language or if it's required to match the direction of the system's language and it's RTL.
1073 bool isLayoutRTL = isLineRTL;
1074 float lineLength = line.width;
1076 // match align for system language direction
1077 if( matchSystemLanguageDirection )
1079 // Swap the alignment type if the line is right to left.
1080 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1082 // Calculate the horizontal line offset.
1083 switch( horizontalAlignment )
1085 case HorizontalAlignment::BEGIN:
1091 lineLength += line.extraLength;
1094 line.alignmentOffset = boxWidth - lineLength;
1098 line.alignmentOffset = 0.f;
1102 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1103 line.alignmentOffset -= line.extraLength;
1108 case HorizontalAlignment::CENTER:
1110 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1114 line.alignmentOffset -= line.extraLength;
1117 line.alignmentOffset = std::floor( line.alignmentOffset ); // floor() avoids pixel alignment issues.
1120 case HorizontalAlignment::END:
1124 line.alignmentOffset = 0.f;
1128 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1129 line.alignmentOffset -= line.extraLength;
1136 lineLength += line.extraLength;
1139 line.alignmentOffset = boxWidth - lineLength;
1146 void Initialize( LineRun& line )
1148 line.glyphRun.glyphIndex = 0u;
1149 line.glyphRun.numberOfGlyphs = 0u;
1150 line.characterRun.characterIndex = 0u;
1151 line.characterRun.numberOfCharacters = 0u;
1153 line.ascender = 0.f;
1154 line.descender = 0.f;
1155 line.extraLength = 0.f;
1156 line.alignmentOffset = 0.f;
1157 line.direction = !RTL;
1158 line.ellipsis = false;
1159 line.lineSpacing = mDefaultLineSpacing;
1164 float mDefaultLineSpacing;
1166 IntrusivePtr<Metrics> mMetrics;
1172 mImpl = new Engine::Impl();
1180 void Engine::SetMetrics( MetricsPtr& metrics )
1182 mImpl->mMetrics = metrics;
1185 void Engine::SetLayout( Type layout )
1187 mImpl->mLayout = layout;
1190 Engine::Type Engine::GetLayout() const
1192 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1193 return mImpl->mLayout;
1196 void Engine::SetCursorWidth( int width )
1198 mImpl->mCursorWidth = static_cast<float>( width );
1201 int Engine::GetCursorWidth() const
1203 return static_cast<int>( mImpl->mCursorWidth );
1206 bool Engine::LayoutText( const Parameters& layoutParameters,
1207 Vector<Vector2>& glyphPositions,
1208 Vector<LineRun>& lines,
1210 bool elideTextEnabled,
1211 bool& isAutoScrollEnabled )
1213 return mImpl->LayoutText( layoutParameters,
1218 isAutoScrollEnabled );
1221 void Engine::ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1222 CharacterIndex startIndex,
1223 Length numberOfCharacters,
1224 Vector<Vector2>& glyphPositions )
1226 mImpl->ReLayoutRightToLeftLines( layoutParameters,
1232 void Engine::Align( const Size& size,
1233 CharacterIndex startIndex,
1234 Length numberOfCharacters,
1235 Text::HorizontalAlignment::Type horizontalAlignment,
1236 Vector<LineRun>& lines,
1237 float& alignmentOffset,
1238 Dali::LayoutDirection::Type layoutDirection,
1239 bool matchSystemLanguageDirection )
1244 horizontalAlignment,
1248 matchSystemLanguageDirection );
1251 void Engine::SetDefaultLineSpacing( float lineSpacing )
1253 mImpl->mDefaultLineSpacing = lineSpacing;
1256 float Engine::GetDefaultLineSpacing() const
1258 return mImpl->mDefaultLineSpacing;
1261 } // namespace Layout
1265 } // namespace Toolkit