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;
52 * @brief Stores temporary layout info of the line.
60 numberOfCharacters( 0u ),
64 wsLengthEndOfLine( 0.f ),
66 descender( MAX_FLOAT )
77 numberOfCharacters = 0u;
81 wsLengthEndOfLine = 0.f;
83 descender = MAX_FLOAT;
86 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
87 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
88 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
89 Length numberOfCharacters; ///< The number of characters which fit in one line.
90 float length; ///< The addition of the advance metric of all the glyphs which fit in one line.
91 float extraBearing; ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
92 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.
93 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
94 float ascender; ///< The maximum ascender of all fonts in the line.
95 float descender; ///< The minimum descender of all fonts in the line.
98 struct LayoutEngine::Impl
101 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
102 mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
103 mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP ),
104 mEllipsisEnabled( false )
106 mFontClient = TextAbstraction::FontClient::Get();
110 * @brief Updates the line ascender and descender with the metrics of a new font.
112 * @param[in] fontId The id of the new font.
113 * @param[in,out] lineLayout The line layout.
115 void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
117 Text::FontMetrics fontMetrics;
118 mFontClient.GetFontMetrics( fontId, fontMetrics );
120 // Sets the maximum ascender.
121 if( fontMetrics.ascender > lineLayout.ascender )
123 lineLayout.ascender = fontMetrics.ascender;
126 // Sets the minimum descender.
127 if( fontMetrics.descender < lineLayout.descender )
129 lineLayout.descender = fontMetrics.descender;
134 * @brief Merges a temporary line layout into the line layout.
136 * @param[in,out] lineLayout The line layout.
137 * @param[in] tmpLineLayout A temporary line layout.
139 void MergeLineLayout( LineLayout& lineLayout,
140 const LineLayout& tmpLineLayout )
142 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
143 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
144 lineLayout.length += tmpLineLayout.length;
146 if( 0.f < tmpLineLayout.length )
148 lineLayout.length += lineLayout.wsLengthEndOfLine;
150 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
154 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
157 if( tmpLineLayout.ascender > lineLayout.ascender )
159 lineLayout.ascender = tmpLineLayout.ascender;
162 if( tmpLineLayout.descender < lineLayout.descender )
164 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 LayoutParameters& 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 );
192 // Stores temporary line layout which has not been added to the final line layout.
193 LineLayout tmpLineLayout;
195 const bool isMultiline = mLayout == MULTI_LINE_BOX;
196 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
198 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
199 // In the case the line starts with a right to left character, if the width is longer than the advance,
200 // the difference needs to be added to the line length.
201 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + lineLayout.glyphIndex );
203 // Set the direction of the first character of the line.
204 lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
205 const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
206 CharacterDirection previousCharacterDirection = firstCharacterDirection;
208 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
209 float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
211 float tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
213 tmpLineLayout.length += 1.f; // Added one unit to give some space to the cursor.
215 // Calculate the line height if there is no characters.
216 FontId lastFontId = glyphInfo.fontId;
217 UpdateLineHeight( lastFontId, tmpLineLayout );
219 bool oneWordLaidOut = false;
221 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
222 glyphIndex < parameters.totalNumberOfGlyphs;
225 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
226 const bool isLastGlyph = glyphIndex == lastGlyphIndex;
228 // Get the glyph info.
229 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
231 // Check if the font of the current glyph is the same of the previous one.
232 // If it's different the ascender and descender need to be updated.
233 if( lastFontId != glyphInfo.fontId )
235 UpdateLineHeight( glyphInfo.fontId, tmpLineLayout );
236 lastFontId = glyphInfo.fontId;
239 // Get the character indices for the current glyph. The last character index is needed
240 // because there are glyphs formed by more than one character but their break info is
241 // given only for the last character.
242 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
243 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
244 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
246 // Get the line break info for the current character.
247 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
249 // Get the word break info for the current character.
250 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
252 // Increase the number of characters.
253 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
255 // Increase the number of glyphs.
256 tmpLineLayout.numberOfGlyphs++;
258 // Check whether is a white space.
259 const Character character = *( parameters.textBuffer + characterFirstIndex );
260 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
262 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
263 const float previousTmpLineLength = tmpLineLayout.length;
264 const float previousTmpExtraBearing = tmpExtraBearing;
265 const float previousTmpExtraWidth = tmpExtraWidth;
267 // Get the character's direction.
268 const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
270 // Increase the accumulated length.
273 // Add the length to the length of white spaces at the end of the line.
274 tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // The advance is used as the width is always zero for the white spaces.
278 // Add as well any previous white space length.
279 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
281 // An extra space may be added to the line for the first and last glyph of the line.
282 // If the bearing of the first glyph is negative, its positive value needs to be added.
283 // If the bearing plus the width of the last glyph is greater than the advance, the difference
284 // needs to be added.
286 if( characterDirection == paragraphDirection )
288 if( RTL == characterDirection )
299 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
312 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
313 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
318 if( characterDirection != previousCharacterDirection )
320 if( RTL == characterDirection )
325 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
326 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
333 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
336 else if( characterDirection == firstCharacterDirection )
338 if( RTL == characterDirection )
344 tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
352 const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
353 tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
358 // Clear the white space length at the end of the line.
359 tmpLineLayout.wsLengthEndOfLine = 0.f;
362 // Check if the accumulated length fits in the width of the box.
363 if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
364 ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
366 // Current word does not fit in the box's width.
367 if( !oneWordLaidOut || completelyFill )
369 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
371 // The word's with doesn't fit in the control's with. It needs to be split by character.
372 if( tmpLineLayout.numberOfGlyphs > 0u )
374 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
375 --tmpLineLayout.numberOfGlyphs;
376 tmpLineLayout.length = previousTmpLineLength;
377 tmpExtraBearing = previousTmpExtraBearing;
378 tmpExtraWidth = previousTmpExtraWidth;
381 // Add part of the word to the line layout.
382 MergeLineLayout( lineLayout, tmpLineLayout );
386 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Current word does not fit.\n" );
389 lineLayout.extraBearing = tmpExtraBearing;
390 lineLayout.extraWidth = tmpExtraWidth;
392 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
397 if( ( isMultiline || isLastGlyph ) &&
398 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
400 // Must break the line. Update the line layout and return.
401 MergeLineLayout( lineLayout, tmpLineLayout );
403 // Set the next paragraph's direction.
405 ( NULL != parameters.characterDirectionBuffer ) )
407 paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
410 lineLayout.extraBearing = tmpExtraBearing;
411 lineLayout.extraWidth = tmpExtraWidth;
413 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Must break\n" );
414 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
419 ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
421 oneWordLaidOut = true;
422 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid out\n" );
424 // Current glyph is the last one of the current word.
425 // Add the temporal layout to the current one.
426 MergeLineLayout( lineLayout, tmpLineLayout );
428 tmpLineLayout.Clear();
431 previousCharacterDirection = characterDirection;
434 lineLayout.extraBearing = tmpExtraBearing;
435 lineLayout.extraWidth = tmpExtraWidth;
437 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
440 void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
441 Length numberOfGlyphs,
443 Vector2* glyphPositionsBuffer )
445 // Traverse the glyphs and set the positions.
447 // Check if the x bearing of the first character is negative.
448 // If it has a negative x bearing, it will exceed the boundaries of the actor,
449 // so the penX position needs to be moved to the right.
451 const GlyphInfo& glyph = *glyphsBuffer;
452 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
453 penX += 1.f; // Added one unit to give some space to the cursor.
455 for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
457 const GlyphInfo& glyph = *( glyphsBuffer + i );
458 Vector2& position = *( glyphPositionsBuffer + i );
460 position.x = penX + glyph.xBearing;
461 position.y = penY - glyph.yBearing;
463 penX += glyph.advance;
467 bool LayoutText( const LayoutParameters& layoutParameters,
468 Vector<Vector2>& glyphPositions,
469 Vector<LineRun>& lines,
472 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
473 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
475 // Set the first paragraph's direction.
476 CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
479 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
481 CharacterDirection currentParagraphDirection = paragraphDirection;
483 // Get the layout for the line.
485 layout.glyphIndex = index;
486 GetLineLayoutForBox( layoutParameters,
491 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex );
492 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex );
493 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs );
494 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters );
495 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " length %f\n", layout.length );
497 if( 0u == layout.numberOfGlyphs )
499 // The width is too small and no characters are laid-out.
500 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
504 // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
506 penY += layout.ascender;
508 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
509 if( mEllipsisEnabled &&
510 ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
511 ( ( mLayout == SINGLE_LINE_BOX ) &&
512 ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) ) )
514 // Do not layout more lines if ellipsis is enabled.
516 // The last line needs to be completely filled with characters.
517 // Part of a word may be used.
519 const Length numberOfLines = lines.Count();
522 LineLayout ellipsisLayout;
523 if( 0u != numberOfLines )
525 // Get the last line and layout it again with the 'completelyFill' flag to true.
526 lineRun = *( lines.Begin() + ( numberOfLines - 1u ) );
528 penY -= layout.ascender - lineRun.descender;
530 ellipsisLayout.glyphIndex = lineRun.glyphRun.glyphIndex;
534 lineRun.glyphRun.glyphIndex = 0u;
535 ellipsisLayout.glyphIndex = 0u;
538 GetLineLayoutForBox( layoutParameters,
540 currentParagraphDirection,
543 lineRun.glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
544 lineRun.characterRun.characterIndex = ellipsisLayout.characterIndex;
545 lineRun.characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
546 lineRun.width = ellipsisLayout.length;
547 lineRun.extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
548 lineRun.ascender = ellipsisLayout.ascender;
549 lineRun.descender = ellipsisLayout.descender;
550 lineRun.direction = !RTL;
551 lineRun.ellipsis = true;
553 actualSize.width = layoutParameters.boundingBox.width;
554 actualSize.height += ( lineRun.ascender + -lineRun.descender );
556 SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun.glyphRun.glyphIndex,
557 ellipsisLayout.numberOfGlyphs,
559 glyphPositions.Begin() + lineRun.glyphRun.glyphIndex );
561 if( 0u != numberOfLines )
563 // Set the last line with the ellipsis layout.
564 *( lines.Begin() + ( numberOfLines - 1u ) ) = lineRun;
569 lines.PushBack( lineRun );
576 const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
579 lineRun.glyphRun.glyphIndex = index;
580 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
581 lineRun.characterRun.characterIndex = layout.characterIndex;
582 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
583 if( isLastLine && !layoutParameters.isLastNewParagraph )
585 const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
586 if( MULTI_LINE_BOX == mLayout )
588 lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
592 lineRun.width = width;
595 lineRun.extraLength = 0.f;
599 lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
600 lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
602 lineRun.ascender = layout.ascender;
603 lineRun.descender = layout.descender;
604 lineRun.direction = !RTL;
605 lineRun.ellipsis = false;
607 lines.PushBack( lineRun );
609 // Update the actual size.
610 if( lineRun.width > actualSize.width )
612 actualSize.width = lineRun.width;
615 actualSize.height += ( lineRun.ascender + -lineRun.descender );
617 SetGlyphPositions( layoutParameters.glyphsBuffer + index,
618 layout.numberOfGlyphs,
620 glyphPositions.Begin() + index );
622 penY += -layout.descender;
624 // Increase the glyph index.
625 index += layout.numberOfGlyphs;
628 layoutParameters.isLastNewParagraph &&
629 ( mLayout == MULTI_LINE_BOX ) )
631 // Need to add a new line with no characters but with height to increase the actualSize.height
632 const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
634 Text::FontMetrics fontMetrics;
635 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
638 lineRun.glyphRun.glyphIndex = 0u;
639 lineRun.glyphRun.numberOfGlyphs = 0u;
640 lineRun.characterRun.characterIndex = 0u;
641 lineRun.characterRun.numberOfCharacters = 0u;
643 lineRun.ascender = fontMetrics.ascender;
644 lineRun.descender = fontMetrics.descender;
645 lineRun.extraLength = 0.f;
646 lineRun.alignmentOffset = 0.f;
647 lineRun.direction = !RTL;
648 lineRun.ellipsis = false;
650 actualSize.height += ( lineRun.ascender + -lineRun.descender );
652 lines.PushBack( lineRun );
655 } // end for() traversing glyphs.
657 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
662 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
663 Vector<Vector2>& glyphPositions )
665 // Traverses the paragraphs with right to left characters.
666 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
668 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
670 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
671 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
673 float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
674 penX += 1.f; // Added one unit to give some space to the cursor.
676 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
678 // Traverses the characters of the right to left paragraph.
679 for( CharacterIndex characterLogicalIndex = 0u;
680 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
681 ++characterLogicalIndex )
683 // Convert the character in the logical order into the character in the visual order.
684 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
686 // Get the number of glyphs of the character.
687 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
689 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
691 // Convert the character in the visual order into the glyph in the visual order.
692 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
694 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
696 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
697 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
699 position.x = penX + glyph.xBearing;
700 penX += glyph.advance;
706 void Align( const Size& layoutSize,
707 Vector<LineRun>& lines )
709 // Traverse all lines and align the glyphs.
711 for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
716 const bool isLastLine = lines.End() == it + 1u;
718 // Calculate the alignment offset accordingly with the align option,
719 // the box width, line length, and the paragraphs direction.
720 CalculateHorizontalAlignment( layoutSize.width,
726 void CalculateHorizontalAlignment( float boxWidth,
730 line.alignmentOffset = 0.f;
731 const bool isRTL = RTL == line.direction;
732 float lineLength = line.width;
734 HorizontalAlignment alignment = mHorizontalAlignment;
736 ( HORIZONTAL_ALIGN_CENTER != alignment ) )
738 if( HORIZONTAL_ALIGN_BEGIN == alignment )
740 alignment = HORIZONTAL_ALIGN_END;
744 alignment = HORIZONTAL_ALIGN_BEGIN;
750 case HORIZONTAL_ALIGN_BEGIN:
752 line.alignmentOffset = 0.f;
756 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
757 line.alignmentOffset -= line.extraLength;
761 line.alignmentOffset += std::min( line.extraLength, boxWidth - lineLength );
766 case HORIZONTAL_ALIGN_CENTER:
768 if( isLastLine && !isRTL )
770 lineLength += line.extraLength;
771 if( lineLength > boxWidth )
773 lineLength = boxWidth;
774 line.alignmentOffset = 0.f;
779 line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
783 line.alignmentOffset -= line.extraLength;
787 line.alignmentOffset += 0.5f * std::min( line.extraLength, boxWidth - lineLength );
791 line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
794 case HORIZONTAL_ALIGN_END:
796 if( isLastLine && !isRTL )
798 lineLength += line.extraLength;
799 if( lineLength > boxWidth )
801 line.alignmentOffset = 0.f;
808 lineLength += line.extraLength;
811 line.alignmentOffset = boxWidth - lineLength;
817 LayoutEngine::Layout mLayout;
818 LayoutEngine::HorizontalAlignment mHorizontalAlignment;
819 LayoutEngine::VerticalAlignment mVerticalAlignment;
821 TextAbstraction::FontClient mFontClient;
823 bool mEllipsisEnabled:1;
826 LayoutEngine::LayoutEngine()
829 mImpl = new LayoutEngine::Impl();
832 LayoutEngine::~LayoutEngine()
837 void LayoutEngine::SetLayout( Layout layout )
839 mImpl->mLayout = layout;
842 unsigned int LayoutEngine::GetLayout() const
844 return mImpl->mLayout;
847 void LayoutEngine::SetTextEllipsisEnabled( bool enabled )
849 mImpl->mEllipsisEnabled = enabled;
852 bool LayoutEngine::GetTextEllipsisEnabled() const
854 return mImpl->mEllipsisEnabled;
857 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
859 mImpl->mHorizontalAlignment = alignment;
862 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
864 return mImpl->mHorizontalAlignment;
867 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
869 mImpl->mVerticalAlignment = alignment;
872 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
874 return mImpl->mVerticalAlignment;
877 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
878 Vector<Vector2>& glyphPositions,
879 Vector<LineRun>& lines,
882 return mImpl->LayoutText( layoutParameters,
888 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
889 Vector<Vector2>& glyphPositions )
891 mImpl->ReLayoutRightToLeftLines( layoutParameters,
895 void LayoutEngine::Align( const Size& layoutSize,
896 Vector<LineRun>& lines )
898 mImpl->Align( layoutSize,
904 } // namespace Toolkit