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 wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
49 float height; ///< The maximum height of all fonts in the line.
50 float ascender; ///< The maximum ascender of all fonts in the line.
53 struct LayoutEngine::Impl
56 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
57 mAlignment( LayoutEngine::ALIGN_BEGIN )
59 mFontClient = TextAbstraction::FontClient::Get();
63 * Retrieves the line layout for a given box width.
65 void GetLineLayoutForBox( const LayoutParameters& parameters,
66 LineLayout& lineLayout )
68 // Initializes the line layout.
69 lineLayout.numberOfCharacters = 0u;
70 lineLayout.numberOfGlyphs = 0u;
71 lineLayout.length = 0.f;
72 lineLayout.wsLengthEndOfLine = 0.f;
73 lineLayout.height = 0.f;
74 lineLayout.ascender = 0.f;
76 // Get the last glyph index.
77 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
79 FontId lastFontId = 0u;
80 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
81 glyphIndex < parameters.totalNumberOfGlyphs;
84 // Get the glyph info.
85 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
87 // Check whether is a white space.
88 const Character character = *( parameters.textBuffer + lineLayout.numberOfCharacters );
89 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
91 // Get the character indices for the current glyph. The last character index is needed
92 // because there are glyphs formed by more than one character but their break info is
93 // given only for the last character.
94 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
96 // Increase the number of characters.
97 lineLayout.numberOfCharacters += charactersPerGlyph;
99 // Increase the number of glyphs.
100 lineLayout.numberOfGlyphs++;
102 // Increase the accumulated length.
103 const float glyphLength = ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
107 // Add the length to the length of white spaces at the end of the line.
108 lineLayout.wsLengthEndOfLine += glyphLength;
112 // Add as well any previous white space length.
113 lineLayout.length += lineLayout.wsLengthEndOfLine + glyphLength;
115 // Clear the white space length at the end of the line.
116 lineLayout.wsLengthEndOfLine = 0.f;
119 if( lastFontId != glyphInfo.fontId )
121 Text::FontMetrics fontMetrics;
122 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
124 // Sets the maximum height.
125 if( fontMetrics.height > lineLayout.height )
127 lineLayout.height = fontMetrics.height;
130 // Sets the maximum ascender.
131 if( fontMetrics.ascender > lineLayout.ascender )
133 lineLayout.ascender = fontMetrics.ascender;
136 lastFontId = glyphInfo.fontId;
142 * Retrieves the line layout for a given box width.
144 void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
145 LineLayout& lineLayout )
147 // Initializes the line layout.
148 lineLayout.numberOfCharacters = 0u;
149 lineLayout.numberOfGlyphs = 0u;
150 lineLayout.length = 0.f;
151 lineLayout.wsLengthEndOfLine = 0.f;
152 lineLayout.height = 0.f;
153 lineLayout.ascender = 0.f;
155 // Stores temporary line layout which has not been added to the final line layout.
156 LineLayout tmpLineLayout;
157 tmpLineLayout.numberOfCharacters = 0u;
158 tmpLineLayout.numberOfGlyphs = 0u;
159 tmpLineLayout.length = 0.f;
160 tmpLineLayout.wsLengthEndOfLine = 0.f;
161 tmpLineLayout.height = 0.f;
162 tmpLineLayout.ascender = 0.f;
164 // Get the last glyph index.
165 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
167 FontId lastFontId = 0u;
168 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
169 glyphIndex < parameters.totalNumberOfGlyphs;
172 // Get the glyph info.
173 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
175 // Get the character indices for the current glyph. The last character index is needed
176 // because there are glyphs formed by more than one character but their break info is
177 // given only for the last character.
178 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
179 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
180 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
182 // Get the line break info for the current character.
183 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
185 // Get the word break info for the current character.
186 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
188 // Increase the number of characters.
189 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
191 // Increase the number of glyphs.
192 tmpLineLayout.numberOfGlyphs++;
194 // Check whether is a white space.
195 const Character character = *( parameters.textBuffer + characterFirstIndex );
196 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
198 // Increase the accumulated length.
201 // Add the length to the length of white spaces at the end of the line.
202 tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // I use the advance as the width is always zero for the white spaces.
206 // Add as well any previous white space length.
207 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
209 // Clear the white space length at the end of the line.
210 tmpLineLayout.wsLengthEndOfLine = 0.f;
213 // Check if the accumulated length fits in the width of the box.
214 if( lineLayout.length + tmpLineLayout.length + ( ( 0.f < tmpLineLayout.length ) ? lineLayout.wsLengthEndOfLine : 0.f ) > parameters.boundingBox.width )
216 // Current word does not fit in the box's width.
220 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
222 // Must break the line. Update the line layout and return.
223 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
224 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
225 lineLayout.length += tmpLineLayout.length;
227 if( 0.f < tmpLineLayout.length )
229 lineLayout.length += lineLayout.wsLengthEndOfLine;
231 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
235 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
238 if( tmpLineLayout.height > lineLayout.height )
240 lineLayout.height = tmpLineLayout.height;
243 if( tmpLineLayout.ascender > lineLayout.ascender )
245 lineLayout.ascender = tmpLineLayout.ascender;
248 tmpLineLayout.numberOfCharacters = 0u;
249 tmpLineLayout.numberOfGlyphs = 0u;
250 tmpLineLayout.length = 0u;
251 tmpLineLayout.wsLengthEndOfLine = 0u;
252 tmpLineLayout.height = 0.f;
253 tmpLineLayout.ascender = 0.f;
257 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
259 // Current glyph is the last one of the current word.
260 // Add the temporal layout to the current one.
261 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
262 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
263 lineLayout.length += tmpLineLayout.length;
264 if( 0.f < tmpLineLayout.length )
266 lineLayout.length += lineLayout.wsLengthEndOfLine;
268 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
272 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
275 if( tmpLineLayout.height > lineLayout.height )
277 lineLayout.height = tmpLineLayout.height;
280 if( tmpLineLayout.ascender > lineLayout.ascender )
282 lineLayout.ascender = tmpLineLayout.ascender;
285 tmpLineLayout.numberOfCharacters = 0u;
286 tmpLineLayout.numberOfGlyphs = 0u;
287 tmpLineLayout.length = 0u;
288 tmpLineLayout.wsLengthEndOfLine = 0u;
289 tmpLineLayout.height = 0.f;
290 tmpLineLayout.ascender = 0.f;
293 if( lastFontId != glyphInfo.fontId )
295 Text::FontMetrics fontMetrics;
296 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
298 // Sets the maximum height.
299 if( fontMetrics.height > tmpLineLayout.height )
301 tmpLineLayout.height = fontMetrics.height;
304 // Sets the maximum ascender.
305 if( fontMetrics.ascender > tmpLineLayout.ascender )
307 tmpLineLayout.ascender = fontMetrics.ascender;
310 lastFontId = glyphInfo.fontId;
315 bool LayoutText( const LayoutParameters& layoutParameters,
316 Vector<Vector2>& glyphPositions,
317 Vector<LineRun>& lines,
320 // TODO Switch between different layouts
325 case LayoutEngine::SINGLE_LINE_BOX:
327 update = SingleLineLayout( layoutParameters,
333 case LayoutEngine::MULTI_LINE_BOX:
335 update = MultiLineLayout( layoutParameters,
348 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
349 Vector<Vector2>& glyphPositions )
351 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
353 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer +lineIndex );
357 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
359 for( CharacterIndex characterLogicalIndex = 0u;
360 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
361 ++characterLogicalIndex )
363 // Convert the character in the logical order into the character in the visual order.
364 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
366 // Get the number of glyphs of the character.
367 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
369 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
371 // Convert the character in the visual order into the glyph in the visual order.
372 GlyphIndex glyphIndex = 1u + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex + index ) - numberOfGlyphs;
374 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
375 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
377 position.x = penX + glyph.xBearing;
378 penX += glyph.advance;
384 void Align( const LayoutParameters& layoutParameters,
385 const Vector<LineRun>& lines,
386 Vector<Vector2>& glyphPositions )
388 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
390 // Traverse all lines and align the glyphs.
391 // LayoutParameters contains bidirectional info for those lines with
392 // right to left text, this info includes the paragraph's direction.
394 LineIndex bidiLineIndex = 0u;
395 for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
399 const LineRun& line = *it;
401 // 1) Get the paragrap's direction.
402 bool paragraphDirection = false;
404 // Check if there is any right to left line.
405 if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
406 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
408 const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
410 // Get the right to left line that match with current line.
411 while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
412 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
415 bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
418 if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
420 paragraphDirection = bidiLine->direction;
424 // 2) Calculate the alignment offset accordingly with the align option,
425 // the box width, line length, and the paragraphs direction.
426 float alignOffset = CalculateAlignment( layoutParameters.boundingBox.width,
429 paragraphDirection );
431 // 3) Traverse all glyphs and update the 'x' position.
432 for( GlyphIndex index = line.glyphIndex,
433 endIndex = line.glyphIndex + line.numberOfGlyphs;
437 Vector2& position = *( glyphPositionsBuffer + index );
439 position.x += alignOffset;
444 bool SingleLineLayout( const LayoutParameters& layoutParameters,
445 Vector<Vector2>& glyphPositions,
446 Vector<LineRun>& lines,
450 layout.glyphIndex = 0u;
451 GetLineLayoutForBox( layoutParameters,
454 // Create a line run and add it to the lines.
455 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
458 lineRun.glyphIndex = 0u;
459 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
460 lineRun.characterRun.characterIndex = 0u;
461 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
462 lineRun.lineSize.width = layout.length;
463 lineRun.lineSize.height = layout.height;
464 lineRun.extraLength = layout.wsLengthEndOfLine;
466 lines.PushBack( lineRun );
468 // Update the actual size.
469 actualSize.width = layout.length;
470 actualSize.height = layout.height;
473 float penY = layout.height;
475 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
476 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
478 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
479 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
481 position.x = penX + glyph.xBearing;
482 position.y = penY - glyph.yBearing;
484 penX += glyph.advance;
490 bool MultiLineLayout( const LayoutParameters& layoutParameters,
491 Vector<Vector2>& glyphPositions,
492 Vector<LineRun>& lines,
496 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
500 // Get the layout for the line.
502 layout.glyphIndex = index;
503 GetMultiLineLayoutForBox( layoutParameters,
506 if( 0u == layout.numberOfGlyphs )
508 // The width is too small and no characters are laid-out.
512 // Create a line run and add it to the lines.
513 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
516 lineRun.glyphIndex = index;
517 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
518 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
519 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
520 lineRun.lineSize.width = layout.length;
521 lineRun.lineSize.height = layout.height;
522 lineRun.extraLength = layout.wsLengthEndOfLine;
524 lines.PushBack( lineRun );
526 // Update the actual size.
527 if( layout.length > actualSize.width )
529 actualSize.width = layout.length;
532 actualSize.height += layout.height;
534 // Traverse the glyphs and set the positions.
536 penY += layout.height;
538 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
539 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
541 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
542 Vector2& position = *( glyphPositionsBuffer + i );
544 position.x = penX + glyph.xBearing;
545 position.y = penY - glyph.yBearing;
547 penX += glyph.advance;
550 // Increase the glyph index.
551 index += layout.numberOfGlyphs;
557 float CalculateAlignment( float boxWidth,
560 bool paragraphDirection )
564 Alignment alignment = mAlignment;
565 if( paragraphDirection &&
566 ( ALIGN_CENTER != alignment ) )
568 if( ALIGN_BEGIN == alignment )
570 alignment = ALIGN_END;
574 alignment = ALIGN_BEGIN;
587 offset = 0.5f * ( boxWidth - lineLength );
588 const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
589 offset = static_cast<float>( intOffset );
594 offset = boxWidth - lineLength;
599 if( paragraphDirection )
601 offset -= extraLength;
607 LayoutEngine::Layout mLayout;
608 LayoutEngine::Alignment mAlignment;
610 TextAbstraction::FontClient mFontClient;
613 LayoutEngine::LayoutEngine()
616 mImpl = new LayoutEngine::Impl();
619 LayoutEngine::~LayoutEngine()
624 void LayoutEngine::SetLayout( Layout layout )
626 mImpl->mLayout = layout;
629 unsigned int LayoutEngine::GetLayout() const
631 return mImpl->mLayout;
634 void LayoutEngine::SetAlignment( Alignment alignment )
636 mImpl->mAlignment = alignment;
639 LayoutEngine::Alignment LayoutEngine::GetAlignment() const
641 return mImpl->mAlignment;
644 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
645 Vector<Vector2>& glyphPositions,
646 Vector<LineRun>& lines,
649 return mImpl->LayoutText( layoutParameters,
655 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
656 Vector<Vector2>& glyphPositions )
658 mImpl->ReLayoutRightToLeftLines( layoutParameters,
662 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
663 const Vector<LineRun>& lines,
664 Vector<Vector2>& glyphPositions )
666 mImpl->Align( layoutParameters,
673 } // namespace Toolkit