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/public-api/math/vector2.h>
24 #include <dali/public-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 const float MAX_FLOAT = std::numeric_limits<float>::max();
47 * @brief Stores temporary layout info of the line.
51 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
52 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
53 Length numberOfCharacters; ///< The number of characters which fit in one line.
54 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
55 float length; ///< The length of the glyphs which fit in one line.
56 float widthAdvanceDiff; ///< The difference between the width and the advance of the last glyph.
57 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
58 float ascender; ///< The maximum ascender of all fonts in the line.
59 float descender; ///< The minimum descender of all fonts in the line.
62 struct LayoutEngine::Impl
65 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
66 mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
67 mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP )
69 mFontClient = TextAbstraction::FontClient::Get();
73 * Retrieves the line layout for a given box width.
75 void GetLineLayoutForBox( const LayoutParameters& parameters,
76 LineLayout& lineLayout )
78 // Initializes the line layout.
79 lineLayout.numberOfCharacters = 0u;
80 lineLayout.numberOfGlyphs = 0u;
81 lineLayout.length = 0.f;
82 lineLayout.wsLengthEndOfLine = 0.f;
83 lineLayout.ascender = 0.f;
84 lineLayout.descender = MAX_FLOAT;
86 // Get the last glyph index.
87 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
89 FontId lastFontId = 0u;
90 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
91 glyphIndex < parameters.totalNumberOfGlyphs;
94 // Get the glyph info.
95 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
97 // Check whether is a white space.
98 const Character character = *( parameters.textBuffer + lineLayout.numberOfCharacters );
99 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
101 // Get the character indices for the current glyph. The last character index is needed
102 // because there are glyphs formed by more than one character but their break info is
103 // given only for the last character.
104 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
106 // Increase the number of characters.
107 lineLayout.numberOfCharacters += charactersPerGlyph;
109 // Increase the number of glyphs.
110 lineLayout.numberOfGlyphs++;
112 // Increase the accumulated length.
113 const float glyphLength = ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
117 // Add the length to the length of white spaces at the end of the line.
118 lineLayout.wsLengthEndOfLine += glyphLength;
122 // Add as well any previous white space length.
123 lineLayout.length += lineLayout.wsLengthEndOfLine + glyphLength;
125 // Clear the white space length at the end of the line.
126 lineLayout.wsLengthEndOfLine = 0.f;
129 if( lastFontId != glyphInfo.fontId )
131 Text::FontMetrics fontMetrics;
132 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
134 // Sets the maximum ascender.
135 if( fontMetrics.ascender > lineLayout.ascender )
137 lineLayout.ascender = fontMetrics.ascender;
140 // Sets the minimum descender.
141 if( fontMetrics.descender < lineLayout.descender )
143 lineLayout.descender = fontMetrics.descender;
146 lastFontId = glyphInfo.fontId;
152 * Retrieves the line layout for a given box width.
154 void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
155 LineLayout& lineLayout )
157 // Initializes the line layout.
158 lineLayout.numberOfCharacters = 0u;
159 lineLayout.numberOfGlyphs = 0u;
160 lineLayout.length = 0.f;
161 lineLayout.widthAdvanceDiff = 0.f;
162 lineLayout.wsLengthEndOfLine = 0.f;
163 lineLayout.ascender = 0.f;
164 lineLayout.descender = MAX_FLOAT;
166 // Stores temporary line layout which has not been added to the final line layout.
167 LineLayout tmpLineLayout;
168 tmpLineLayout.numberOfCharacters = 0u;
169 tmpLineLayout.numberOfGlyphs = 0u;
170 tmpLineLayout.length = 0.f;
171 tmpLineLayout.widthAdvanceDiff = 0.f;
172 tmpLineLayout.wsLengthEndOfLine = 0.f;
173 tmpLineLayout.ascender = 0.f;
174 tmpLineLayout.descender = MAX_FLOAT;
176 FontId lastFontId = 0u;
177 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
178 glyphIndex < parameters.totalNumberOfGlyphs;
181 // Get the glyph info.
182 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
184 // Get the character indices for the current glyph. The last character index is needed
185 // because there are glyphs formed by more than one character but their break info is
186 // given only for the last character.
187 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
188 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
189 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
191 // Get the line break info for the current character.
192 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
194 // Get the word break info for the current character.
195 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
197 // Increase the number of characters.
198 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
200 // Increase the number of glyphs.
201 tmpLineLayout.numberOfGlyphs++;
203 // Check whether is a white space.
204 const Character character = *( parameters.textBuffer + characterFirstIndex );
205 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
207 // Increase the accumulated length.
210 // Add the length to the length of white spaces at the end of the line.
211 tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // I use the advance as the width is always zero for the white spaces.
212 tmpLineLayout.widthAdvanceDiff = 0.f;
216 // Add as well any previous white space length.
217 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
218 tmpLineLayout.widthAdvanceDiff = glyphInfo.width - glyphInfo.advance;
220 // Clear the white space length at the end of the line.
221 tmpLineLayout.wsLengthEndOfLine = 0.f;
224 // Check if the accumulated length fits in the width of the box.
225 if( lineLayout.length + tmpLineLayout.length + tmpLineLayout.widthAdvanceDiff + ( ( 0.f < tmpLineLayout.length ) ? lineLayout.wsLengthEndOfLine : 0.f ) > parameters.boundingBox.width )
227 // Current word does not fit in the box's width.
231 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
233 // Must break the line. Update the line layout and return.
234 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
235 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
236 lineLayout.length += tmpLineLayout.length;
237 lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
239 if( 0.f < tmpLineLayout.length )
241 lineLayout.length += lineLayout.wsLengthEndOfLine;
243 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
247 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
250 if( tmpLineLayout.ascender > lineLayout.ascender )
252 lineLayout.ascender = tmpLineLayout.ascender;
255 if( tmpLineLayout.descender < lineLayout.descender )
257 lineLayout.descender = tmpLineLayout.descender;
260 tmpLineLayout.numberOfCharacters = 0u;
261 tmpLineLayout.numberOfGlyphs = 0u;
262 tmpLineLayout.length = 0u;
263 tmpLineLayout.widthAdvanceDiff = 0u;
264 tmpLineLayout.wsLengthEndOfLine = 0u;
265 tmpLineLayout.ascender = 0.f;
266 tmpLineLayout.descender = MAX_FLOAT;
270 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
272 // Current glyph is the last one of the current word.
273 // Add the temporal layout to the current one.
274 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
275 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
276 lineLayout.length += tmpLineLayout.length;
277 lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
279 if( 0.f < tmpLineLayout.length )
281 lineLayout.length += lineLayout.wsLengthEndOfLine;
283 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
287 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
290 if( tmpLineLayout.ascender > lineLayout.ascender )
292 lineLayout.ascender = tmpLineLayout.ascender;
295 if( tmpLineLayout.descender < lineLayout.descender )
297 lineLayout.descender = tmpLineLayout.descender;
300 tmpLineLayout.numberOfCharacters = 0u;
301 tmpLineLayout.numberOfGlyphs = 0u;
302 tmpLineLayout.length = 0u;
303 tmpLineLayout.widthAdvanceDiff = 0u;
304 tmpLineLayout.wsLengthEndOfLine = 0u;
305 tmpLineLayout.ascender = 0.f;
306 tmpLineLayout.descender = MAX_FLOAT;
309 if( lastFontId != glyphInfo.fontId )
311 Text::FontMetrics fontMetrics;
312 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
314 // Sets the maximum ascender.
315 if( fontMetrics.ascender > tmpLineLayout.ascender )
317 tmpLineLayout.ascender = fontMetrics.ascender;
320 // Sets the minimum descender.
321 if( -fontMetrics.descender < tmpLineLayout.descender )
323 tmpLineLayout.descender = fontMetrics.descender;
326 lastFontId = glyphInfo.fontId;
331 bool LayoutText( const LayoutParameters& layoutParameters,
332 Vector<Vector2>& glyphPositions,
333 Vector<LineRun>& lines,
336 // TODO Switch between different layouts
341 case LayoutEngine::SINGLE_LINE_BOX:
343 update = SingleLineLayout( layoutParameters,
349 case LayoutEngine::MULTI_LINE_BOX:
351 update = MultiLineLayout( layoutParameters,
364 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
365 Vector<Vector2>& glyphPositions )
367 // Traverses the paragraphs with right to left characters.
368 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
370 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
374 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
376 // Traverses the characters of the right to left paragraph.
377 for( CharacterIndex characterLogicalIndex = 0u;
378 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
379 ++characterLogicalIndex )
381 // Convert the character in the logical order into the character in the visual order.
382 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
384 // Get the number of glyphs of the character.
385 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
387 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
389 // Convert the character in the visual order into the glyph in the visual order.
390 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
392 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
394 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
395 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
397 position.x = penX + glyph.xBearing;
398 penX += glyph.advance;
404 void Align( const LayoutParameters& layoutParameters,
405 const Size& layoutSize,
406 const Vector<LineRun>& lines,
407 Vector<Vector2>& glyphPositions )
409 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
411 // Traverse all lines and align the glyphs.
412 // LayoutParameters contains bidirectional info for those lines with
413 // right to left text, this info includes the paragraph's direction.
415 LineIndex bidiLineIndex = 0u;
416 for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
420 const LineRun& line = *it;
422 // 1) Get the paragrap's direction.
423 bool paragraphDirection = false;
425 // Check if there is any right to left line.
426 if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
427 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
429 const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
431 // Get the right to left line that match with current line.
432 while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
433 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
436 bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
439 if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
441 paragraphDirection = bidiLine->direction;
445 // 2) Calculate the alignment offset accordingly with the align option,
446 // the box width, line length, and the paragraphs direction.
447 float alignOffset = CalculateHorizontalAlignment( layoutSize.width,
450 paragraphDirection );
452 // 3) Traverse all glyphs and update the 'x' position.
453 for( GlyphIndex index = line.glyphIndex,
454 endIndex = line.glyphIndex + line.numberOfGlyphs;
458 Vector2& position = *( glyphPositionsBuffer + index );
460 position.x += alignOffset;
465 bool SingleLineLayout( const LayoutParameters& layoutParameters,
466 Vector<Vector2>& glyphPositions,
467 Vector<LineRun>& lines,
471 layout.glyphIndex = 0u;
472 GetLineLayoutForBox( layoutParameters,
475 // Create a line run and add it to the lines.
476 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
479 lineRun.glyphIndex = 0u;
480 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
481 lineRun.characterRun.characterIndex = 0u;
482 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
483 lineRun.width = layout.length;
484 lineRun.ascender = layout.ascender;
485 lineRun.descender = layout.descender;
486 lineRun.extraLength = layout.wsLengthEndOfLine;
487 lineRun.direction = false;
489 lines.PushBack( lineRun );
491 // Update the actual size.
492 actualSize.width = layout.length;
493 actualSize.height = lineRun.ascender + -lineRun.descender;
496 float penY = layout.ascender;
498 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
499 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
501 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
502 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
504 position.x = penX + glyph.xBearing;
505 position.y = penY - glyph.yBearing;
507 penX += glyph.advance;
513 bool MultiLineLayout( const LayoutParameters& layoutParameters,
514 Vector<Vector2>& glyphPositions,
515 Vector<LineRun>& lines,
519 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
523 // Get the layout for the line.
525 layout.glyphIndex = index;
526 GetMultiLineLayoutForBox( layoutParameters,
529 if( 0u == layout.numberOfGlyphs )
531 // The width is too small and no characters are laid-out.
535 // Create a line run and add it to the lines.
536 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
539 lineRun.glyphIndex = index;
540 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
541 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
542 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
543 lineRun.width = layout.length + ( ( layout.widthAdvanceDiff > 0.f ) ? layout.widthAdvanceDiff : 0.f );
544 lineRun.ascender = layout.ascender;
545 lineRun.descender = layout.descender;
546 lineRun.extraLength = layout.wsLengthEndOfLine;
547 lineRun.direction = false;
549 lines.PushBack( lineRun );
551 // Update the actual size.
552 if( layout.length + layout.widthAdvanceDiff > actualSize.width )
554 actualSize.width = layout.length;
557 actualSize.height += ( lineRun.ascender + -lineRun.descender );
559 // Traverse the glyphs and set the positions.
561 penY += layout.ascender;
563 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
564 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
566 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
567 Vector2& position = *( glyphPositionsBuffer + i );
569 position.x = penX + glyph.xBearing;
570 position.y = penY - glyph.yBearing;
572 penX += glyph.advance;
575 penY += -layout.descender;
577 // Increase the glyph index.
578 index += layout.numberOfGlyphs;
584 float CalculateHorizontalAlignment( float boxWidth,
587 bool paragraphDirection )
591 HorizontalAlignment alignment = mHorizontalAlignment;
592 if( paragraphDirection &&
593 ( HORIZONTAL_ALIGN_CENTER != alignment ) )
595 if( HORIZONTAL_ALIGN_BEGIN == alignment )
597 alignment = HORIZONTAL_ALIGN_END;
601 alignment = HORIZONTAL_ALIGN_BEGIN;
607 case HORIZONTAL_ALIGN_BEGIN:
612 case HORIZONTAL_ALIGN_CENTER:
614 offset = 0.5f * ( boxWidth - lineLength );
615 const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
616 offset = static_cast<float>( intOffset );
619 case HORIZONTAL_ALIGN_END:
621 offset = boxWidth - lineLength;
626 if( paragraphDirection )
628 offset -= extraLength;
634 LayoutEngine::Layout mLayout;
635 LayoutEngine::HorizontalAlignment mHorizontalAlignment;
636 LayoutEngine::VerticalAlignment mVerticalAlignment;
638 TextAbstraction::FontClient mFontClient;
641 LayoutEngine::LayoutEngine()
644 mImpl = new LayoutEngine::Impl();
647 LayoutEngine::~LayoutEngine()
652 void LayoutEngine::SetLayout( Layout layout )
654 mImpl->mLayout = layout;
657 unsigned int LayoutEngine::GetLayout() const
659 return mImpl->mLayout;
662 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
664 mImpl->mHorizontalAlignment = alignment;
667 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
669 return mImpl->mHorizontalAlignment;
672 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
674 mImpl->mVerticalAlignment = alignment;
677 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
679 return mImpl->mVerticalAlignment;
682 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
683 Vector<Vector2>& glyphPositions,
684 Vector<LineRun>& lines,
687 return mImpl->LayoutText( layoutParameters,
693 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
694 Vector<Vector2>& glyphPositions )
696 mImpl->ReLayoutRightToLeftLines( layoutParameters,
700 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
701 const Size& layoutSize,
702 const Vector<LineRun>& lines,
703 Vector<Vector2>& glyphPositions )
705 mImpl->Align( layoutParameters,
713 } // namespace Toolkit