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>
22 #include <dali/public-api/math/vector2.h>
23 #include <dali/public-api/text-abstraction/font-client.h>
26 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
27 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
39 * @brief Stores temporary layout info of the line.
43 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
44 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
45 Length numberOfCharacters; ///< The number of characters which fit in one line.
46 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
47 float length; ///< The length of the glyphs which fit in one line.
48 float widthAdvanceDiff; ///< The difference between the width and the advance of the last glyph.
49 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
50 float ascender; ///< The maximum ascender of all fonts in the line.
51 float descender; ///< The maximum descender of all fonts in the line.
54 struct LayoutEngine::Impl
57 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
58 mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
59 mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP )
61 mFontClient = TextAbstraction::FontClient::Get();
65 * Retrieves the line layout for a given box width.
67 void GetLineLayoutForBox( const LayoutParameters& parameters,
68 LineLayout& lineLayout )
70 // Initializes the line layout.
71 lineLayout.numberOfCharacters = 0u;
72 lineLayout.numberOfGlyphs = 0u;
73 lineLayout.length = 0.f;
74 lineLayout.wsLengthEndOfLine = 0.f;
75 lineLayout.ascender = 0.f;
76 lineLayout.descender = 0.f;
78 // Get the last glyph index.
79 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
81 FontId lastFontId = 0u;
82 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
83 glyphIndex < parameters.totalNumberOfGlyphs;
86 // Get the glyph info.
87 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
89 // Check whether is a white space.
90 const Character character = *( parameters.textBuffer + lineLayout.numberOfCharacters );
91 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
93 // Get the character indices for the current glyph. The last character index is needed
94 // because there are glyphs formed by more than one character but their break info is
95 // given only for the last character.
96 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
98 // Increase the number of characters.
99 lineLayout.numberOfCharacters += charactersPerGlyph;
101 // Increase the number of glyphs.
102 lineLayout.numberOfGlyphs++;
104 // Increase the accumulated length.
105 const float glyphLength = ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
109 // Add the length to the length of white spaces at the end of the line.
110 lineLayout.wsLengthEndOfLine += glyphLength;
114 // Add as well any previous white space length.
115 lineLayout.length += lineLayout.wsLengthEndOfLine + glyphLength;
117 // Clear the white space length at the end of the line.
118 lineLayout.wsLengthEndOfLine = 0.f;
121 if( lastFontId != glyphInfo.fontId )
123 Text::FontMetrics fontMetrics;
124 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
126 // Sets the maximum ascender.
127 if( fontMetrics.ascender > lineLayout.ascender )
129 lineLayout.ascender = fontMetrics.ascender;
132 // Sets the maximum descender.
133 if( fontMetrics.descender > lineLayout.descender )
135 lineLayout.descender = fontMetrics.descender;
138 lastFontId = glyphInfo.fontId;
144 * Retrieves the line layout for a given box width.
146 void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
147 LineLayout& lineLayout )
149 // Initializes the line layout.
150 lineLayout.numberOfCharacters = 0u;
151 lineLayout.numberOfGlyphs = 0u;
152 lineLayout.length = 0.f;
153 lineLayout.widthAdvanceDiff = 0.f;
154 lineLayout.wsLengthEndOfLine = 0.f;
155 lineLayout.ascender = 0.f;
156 lineLayout.descender = 0.f;
158 // Stores temporary line layout which has not been added to the final line layout.
159 LineLayout tmpLineLayout;
160 tmpLineLayout.numberOfCharacters = 0u;
161 tmpLineLayout.numberOfGlyphs = 0u;
162 tmpLineLayout.length = 0.f;
163 tmpLineLayout.widthAdvanceDiff = 0.f;
164 tmpLineLayout.wsLengthEndOfLine = 0.f;
165 tmpLineLayout.ascender = 0.f;
166 tmpLineLayout.descender = 0.f;
168 FontId lastFontId = 0u;
169 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
170 glyphIndex < parameters.totalNumberOfGlyphs;
173 // Get the glyph info.
174 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
176 // Get the character indices for the current glyph. The last character index is needed
177 // because there are glyphs formed by more than one character but their break info is
178 // given only for the last character.
179 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
180 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
181 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
183 // Get the line break info for the current character.
184 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
186 // Get the word break info for the current character.
187 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
189 // Increase the number of characters.
190 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
192 // Increase the number of glyphs.
193 tmpLineLayout.numberOfGlyphs++;
195 // Check whether is a white space.
196 const Character character = *( parameters.textBuffer + characterFirstIndex );
197 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
199 // Increase the accumulated length.
202 // Add the length to the length of white spaces at the end of the line.
203 tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // I use the advance as the width is always zero for the white spaces.
204 tmpLineLayout.widthAdvanceDiff = 0.f;
208 // Add as well any previous white space length.
209 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
210 tmpLineLayout.widthAdvanceDiff = glyphInfo.width - glyphInfo.advance;
212 // Clear the white space length at the end of the line.
213 tmpLineLayout.wsLengthEndOfLine = 0.f;
216 // Check if the accumulated length fits in the width of the box.
217 if( lineLayout.length + tmpLineLayout.length + tmpLineLayout.widthAdvanceDiff + ( ( 0.f < tmpLineLayout.length ) ? lineLayout.wsLengthEndOfLine : 0.f ) > parameters.boundingBox.width )
219 // Current word does not fit in the box's width.
223 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
225 // Must break the line. Update the line layout and return.
226 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
227 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
228 lineLayout.length += tmpLineLayout.length;
229 lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
231 if( 0.f < tmpLineLayout.length )
233 lineLayout.length += lineLayout.wsLengthEndOfLine;
235 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
239 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
242 if( tmpLineLayout.ascender > lineLayout.ascender )
244 lineLayout.ascender = tmpLineLayout.ascender;
247 if( tmpLineLayout.descender > lineLayout.descender )
249 lineLayout.descender = tmpLineLayout.descender;
252 tmpLineLayout.numberOfCharacters = 0u;
253 tmpLineLayout.numberOfGlyphs = 0u;
254 tmpLineLayout.length = 0u;
255 tmpLineLayout.widthAdvanceDiff = 0u;
256 tmpLineLayout.wsLengthEndOfLine = 0u;
257 tmpLineLayout.ascender = 0.f;
258 tmpLineLayout.descender = 0.f;
262 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
264 // Current glyph is the last one of the current word.
265 // Add the temporal layout to the current one.
266 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
267 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
268 lineLayout.length += tmpLineLayout.length;
269 lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
271 if( 0.f < tmpLineLayout.length )
273 lineLayout.length += lineLayout.wsLengthEndOfLine;
275 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
279 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
282 if( tmpLineLayout.ascender > lineLayout.ascender )
284 lineLayout.ascender = tmpLineLayout.ascender;
287 if( tmpLineLayout.descender > lineLayout.descender )
289 lineLayout.descender = tmpLineLayout.descender;
292 tmpLineLayout.numberOfCharacters = 0u;
293 tmpLineLayout.numberOfGlyphs = 0u;
294 tmpLineLayout.length = 0u;
295 tmpLineLayout.widthAdvanceDiff = 0u;
296 tmpLineLayout.wsLengthEndOfLine = 0u;
297 tmpLineLayout.ascender = 0.f;
298 tmpLineLayout.descender = 0.f;
301 if( lastFontId != glyphInfo.fontId )
303 Text::FontMetrics fontMetrics;
304 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
306 // Sets the maximum ascender.
307 if( fontMetrics.ascender > tmpLineLayout.ascender )
309 tmpLineLayout.ascender = fontMetrics.ascender;
312 // Sets the maximum descender.
313 if( -fontMetrics.descender > tmpLineLayout.descender )
315 tmpLineLayout.descender = -fontMetrics.descender;
318 lastFontId = glyphInfo.fontId;
323 bool LayoutText( const LayoutParameters& layoutParameters,
324 Vector<Vector2>& glyphPositions,
325 Vector<LineRun>& lines,
328 // TODO Switch between different layouts
333 case LayoutEngine::SINGLE_LINE_BOX:
335 update = SingleLineLayout( layoutParameters,
341 case LayoutEngine::MULTI_LINE_BOX:
343 update = MultiLineLayout( layoutParameters,
356 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
357 Vector<Vector2>& glyphPositions )
359 // Traverses the paragraphs with right to left characters.
360 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
362 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
366 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
368 // Traverses the characters of the right to left paragraph.
369 for( CharacterIndex characterLogicalIndex = 0u;
370 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
371 ++characterLogicalIndex )
373 // Convert the character in the logical order into the character in the visual order.
374 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
376 // Get the number of glyphs of the character.
377 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
379 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
381 // Convert the character in the visual order into the glyph in the visual order.
382 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
384 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
386 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
387 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
389 position.x = penX + glyph.xBearing;
390 penX += glyph.advance;
396 void Align( const LayoutParameters& layoutParameters,
397 const Size& layoutSize,
398 const Vector<LineRun>& lines,
399 Vector<Vector2>& glyphPositions )
401 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
403 // Traverse all lines and align the glyphs.
404 // LayoutParameters contains bidirectional info for those lines with
405 // right to left text, this info includes the paragraph's direction.
407 LineIndex bidiLineIndex = 0u;
408 for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
412 const LineRun& line = *it;
414 // 1) Get the paragrap's direction.
415 bool paragraphDirection = false;
417 // Check if there is any right to left line.
418 if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
419 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
421 const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
423 // Get the right to left line that match with current line.
424 while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
425 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
428 bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
431 if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
433 paragraphDirection = bidiLine->direction;
437 // 2) Calculate the alignment offset accordingly with the align option,
438 // the box width, line length, and the paragraphs direction.
439 float alignOffset = CalculateHorizontalAlignment( layoutSize.width,
442 paragraphDirection );
444 // 3) Traverse all glyphs and update the 'x' position.
445 for( GlyphIndex index = line.glyphIndex,
446 endIndex = line.glyphIndex + line.numberOfGlyphs;
450 Vector2& position = *( glyphPositionsBuffer + index );
452 position.x += alignOffset;
457 bool SingleLineLayout( const LayoutParameters& layoutParameters,
458 Vector<Vector2>& glyphPositions,
459 Vector<LineRun>& lines,
463 layout.glyphIndex = 0u;
464 GetLineLayoutForBox( layoutParameters,
467 // Create a line run and add it to the lines.
468 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
471 lineRun.glyphIndex = 0u;
472 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
473 lineRun.characterRun.characterIndex = 0u;
474 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
475 lineRun.lineSize.width = layout.length;
476 lineRun.lineSize.height = layout.ascender + layout.descender;
477 lineRun.extraLength = layout.wsLengthEndOfLine;
479 lines.PushBack( lineRun );
481 // Update the actual size.
482 actualSize.width = layout.length;
483 actualSize.height = lineRun.lineSize.height;
486 float penY = layout.ascender;
488 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
489 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
491 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
492 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
494 position.x = penX + glyph.xBearing;
495 position.y = penY - glyph.yBearing;
497 penX += glyph.advance;
503 bool MultiLineLayout( const LayoutParameters& layoutParameters,
504 Vector<Vector2>& glyphPositions,
505 Vector<LineRun>& lines,
509 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
513 // Get the layout for the line.
515 layout.glyphIndex = index;
516 GetMultiLineLayoutForBox( layoutParameters,
519 if( 0u == layout.numberOfGlyphs )
521 // The width is too small and no characters are laid-out.
525 // Create a line run and add it to the lines.
526 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
529 lineRun.glyphIndex = index;
530 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
531 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
532 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
533 lineRun.lineSize.width = layout.length + ( ( layout.widthAdvanceDiff > 0.f ) ? layout.widthAdvanceDiff : 0.f );
534 lineRun.lineSize.height = layout.ascender + layout.descender;
535 lineRun.extraLength = layout.wsLengthEndOfLine;
537 lines.PushBack( lineRun );
539 // Update the actual size.
540 if( layout.length + layout.widthAdvanceDiff > actualSize.width )
542 actualSize.width = layout.length;
545 actualSize.height += lineRun.lineSize.height;
547 // Traverse the glyphs and set the positions.
549 penY += layout.ascender;
551 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
552 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
554 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
555 Vector2& position = *( glyphPositionsBuffer + i );
557 position.x = penX + glyph.xBearing;
558 position.y = penY - glyph.yBearing;
560 penX += glyph.advance;
563 penY += layout.descender;
565 // Increase the glyph index.
566 index += layout.numberOfGlyphs;
572 float CalculateHorizontalAlignment( float boxWidth,
575 bool paragraphDirection )
579 HorizontalAlignment alignment = mHorizontalAlignment;
580 if( paragraphDirection &&
581 ( HORIZONTAL_ALIGN_CENTER != alignment ) )
583 if( HORIZONTAL_ALIGN_BEGIN == alignment )
585 alignment = HORIZONTAL_ALIGN_END;
589 alignment = HORIZONTAL_ALIGN_BEGIN;
595 case HORIZONTAL_ALIGN_BEGIN:
600 case HORIZONTAL_ALIGN_CENTER:
602 offset = 0.5f * ( boxWidth - lineLength );
603 const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
604 offset = static_cast<float>( intOffset );
607 case HORIZONTAL_ALIGN_END:
609 offset = boxWidth - lineLength;
614 if( paragraphDirection )
616 offset -= extraLength;
622 LayoutEngine::Layout mLayout;
623 LayoutEngine::HorizontalAlignment mHorizontalAlignment;
624 LayoutEngine::VerticalAlignment mVerticalAlignment;
626 TextAbstraction::FontClient mFontClient;
629 LayoutEngine::LayoutEngine()
632 mImpl = new LayoutEngine::Impl();
635 LayoutEngine::~LayoutEngine()
640 void LayoutEngine::SetLayout( Layout layout )
642 mImpl->mLayout = layout;
645 unsigned int LayoutEngine::GetLayout() const
647 return mImpl->mLayout;
650 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
652 mImpl->mHorizontalAlignment = alignment;
655 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
657 return mImpl->mHorizontalAlignment;
660 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
662 mImpl->mVerticalAlignment = alignment;
665 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
667 return mImpl->mVerticalAlignment;
670 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
671 Vector<Vector2>& glyphPositions,
672 Vector<LineRun>& lines,
675 return mImpl->LayoutText( layoutParameters,
681 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
682 Vector<Vector2>& glyphPositions )
684 mImpl->ReLayoutRightToLeftLines( layoutParameters,
688 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
689 const Size& layoutSize,
690 const Vector<LineRun>& lines,
691 Vector<Vector2>& glyphPositions )
693 mImpl->Align( layoutParameters,
701 } // namespace Toolkit