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 if( glyphIndex == lastGlyphIndex )
224 // Must break the line. Update the line layout and return.
225 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
226 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
227 lineLayout.length += tmpLineLayout.length;
229 if( 0.f < tmpLineLayout.length )
231 lineLayout.length += lineLayout.wsLengthEndOfLine;
233 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
237 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
240 if( tmpLineLayout.height > lineLayout.height )
242 lineLayout.height = tmpLineLayout.height;
245 if( tmpLineLayout.ascender > lineLayout.ascender )
247 lineLayout.ascender = tmpLineLayout.ascender;
251 tmpLineLayout.numberOfCharacters = 0u;
252 tmpLineLayout.numberOfGlyphs = 0u;
253 tmpLineLayout.length = 0u;
254 tmpLineLayout.wsLengthEndOfLine = 0u;
255 tmpLineLayout.height = 0.f;
256 tmpLineLayout.ascender = 0.f;
260 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
262 // Current glyph is the last one of the current word.
263 // Add the temporal layout to the current one.
264 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
265 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
266 lineLayout.length += tmpLineLayout.length;
267 if( 0.f < tmpLineLayout.length )
269 lineLayout.length += lineLayout.wsLengthEndOfLine;
271 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
275 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
278 if( tmpLineLayout.height > lineLayout.height )
280 lineLayout.height = tmpLineLayout.height;
283 if( tmpLineLayout.ascender > lineLayout.ascender )
285 lineLayout.ascender = tmpLineLayout.ascender;
288 tmpLineLayout.numberOfCharacters = 0u;
289 tmpLineLayout.numberOfGlyphs = 0u;
290 tmpLineLayout.length = 0u;
291 tmpLineLayout.wsLengthEndOfLine = 0u;
292 tmpLineLayout.height = 0.f;
293 tmpLineLayout.ascender = 0.f;
296 if( lastFontId != glyphInfo.fontId )
298 Text::FontMetrics fontMetrics;
299 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
301 // Sets the maximum height.
302 if( fontMetrics.height > tmpLineLayout.height )
304 tmpLineLayout.height = fontMetrics.height;
307 // Sets the maximum ascender.
308 if( fontMetrics.ascender > tmpLineLayout.ascender )
310 tmpLineLayout.ascender = fontMetrics.ascender;
313 lastFontId = glyphInfo.fontId;
318 bool LayoutText( const LayoutParameters& layoutParameters,
319 Vector<Vector2>& glyphPositions,
320 Vector<LineRun>& lines,
323 // TODO Switch between different layouts
328 case LayoutEngine::SINGLE_LINE_BOX:
330 update = SingleLineLayout( layoutParameters,
336 case LayoutEngine::MULTI_LINE_BOX:
338 update = MultiLineLayout( layoutParameters,
351 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
352 Vector<Vector2>& glyphPositions )
354 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
356 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer +lineIndex );
360 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
362 for( CharacterIndex characterLogicalIndex = 0u;
363 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
364 ++characterLogicalIndex )
366 // Convert the character in the logical order into the character in the visual order.
367 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
369 // Get the number of glyphs of the character.
370 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
372 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
374 // Convert the character in the visual order into the glyph in the visual order.
375 GlyphIndex glyphIndex = 1u + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex + index ) - numberOfGlyphs;
377 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
378 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
380 position.x = penX + glyph.xBearing;
381 penX += glyph.advance;
387 void Align( const LayoutParameters& layoutParameters,
388 const Vector<LineRun>& lines,
389 Vector<Vector2>& glyphPositions )
391 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
393 // Traverse all lines and align the glyphs.
394 // LayoutParameters contains bidirectional info for those lines with
395 // right to left text, this info includes the paragraph's direction.
397 LineIndex bidiLineIndex = 0u;
398 for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
402 const LineRun& line = *it;
404 // 1) Get the paragrap's direction.
405 bool paragraphDirection = false;
407 // Check if there is any right to left line.
408 if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
409 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
411 const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
413 // Get the right to left line that match with current line.
414 while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
415 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
418 bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
421 if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
423 paragraphDirection = bidiLine->direction;
427 // 2) Calculate the alignment offset accordingly with the align option,
428 // the box width, line length, and the paragraphs direction.
429 float alignOffset = CalculateAlignment( layoutParameters.boundingBox.width,
432 paragraphDirection );
434 // 3) Traverse all glyphs and update the 'x' position.
435 for( GlyphIndex index = line.glyphIndex,
436 endIndex = line.glyphIndex + line.numberOfGlyphs;
440 Vector2& position = *( glyphPositionsBuffer + index );
442 position.x += alignOffset;
447 bool SingleLineLayout( const LayoutParameters& layoutParameters,
448 Vector<Vector2>& glyphPositions,
449 Vector<LineRun>& lines,
453 layout.glyphIndex = 0u;
454 GetLineLayoutForBox( layoutParameters,
457 // Create a line run and add it to the lines.
458 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
461 lineRun.glyphIndex = 0u;
462 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
463 lineRun.characterRun.characterIndex = 0u;
464 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
465 lineRun.lineSize.width = layout.length;
466 lineRun.lineSize.height = layout.height;
467 lineRun.extraLength = layout.wsLengthEndOfLine;
469 lines.PushBack( lineRun );
471 // Update the actual size.
472 actualSize.width = layout.length;
473 actualSize.height = layout.height;
476 float penY = layout.height;
478 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
479 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
481 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
482 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
484 position.x = penX + glyph.xBearing;
485 position.y = penY - glyph.yBearing;
487 penX += glyph.advance;
493 bool MultiLineLayout( const LayoutParameters& layoutParameters,
494 Vector<Vector2>& glyphPositions,
495 Vector<LineRun>& lines,
499 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
503 // Get the layout for the line.
505 layout.glyphIndex = index;
506 GetMultiLineLayoutForBox( layoutParameters,
509 if( 0u == layout.numberOfGlyphs )
511 // The width is too small and no characters are laid-out.
515 // Create a line run and add it to the lines.
516 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
519 lineRun.glyphIndex = index;
520 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
521 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
522 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
523 lineRun.lineSize.width = layout.length;
524 lineRun.lineSize.height = layout.height;
525 lineRun.extraLength = layout.wsLengthEndOfLine;
527 lines.PushBack( lineRun );
529 // Update the actual size.
530 if( layout.length > actualSize.width )
532 actualSize.width = layout.length;
535 actualSize.height += layout.height;
537 // Traverse the glyphs and set the positions.
539 penY += layout.height;
541 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
542 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
544 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
545 Vector2& position = *( glyphPositionsBuffer + i );
547 position.x = penX + glyph.xBearing;
548 position.y = penY - glyph.yBearing;
550 penX += glyph.advance;
553 // Increase the glyph index.
554 index += layout.numberOfGlyphs;
560 float CalculateAlignment( float boxWidth,
563 bool paragraphDirection )
567 Alignment alignment = mAlignment;
568 if( paragraphDirection &&
569 ( ALIGN_CENTER != alignment ) )
571 if( ALIGN_BEGIN == alignment )
573 alignment = ALIGN_END;
577 alignment = ALIGN_BEGIN;
590 offset = 0.5f * ( boxWidth - lineLength );
591 const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
592 offset = static_cast<float>( intOffset );
597 offset = boxWidth - lineLength;
602 if( paragraphDirection )
604 offset -= extraLength;
610 LayoutEngine::Layout mLayout;
611 LayoutEngine::Alignment mAlignment;
613 TextAbstraction::FontClient mFontClient;
616 LayoutEngine::LayoutEngine()
619 mImpl = new LayoutEngine::Impl();
622 LayoutEngine::~LayoutEngine()
627 void LayoutEngine::SetLayout( Layout layout )
629 mImpl->mLayout = layout;
632 unsigned int LayoutEngine::GetLayout() const
634 return mImpl->mLayout;
637 void LayoutEngine::SetAlignment( Alignment alignment )
639 mImpl->mAlignment = alignment;
642 LayoutEngine::Alignment LayoutEngine::GetAlignment() const
644 return mImpl->mAlignment;
647 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
648 Vector<Vector2>& glyphPositions,
649 Vector<LineRun>& lines,
652 return mImpl->LayoutText( layoutParameters,
658 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
659 Vector<Vector2>& glyphPositions )
661 mImpl->ReLayoutRightToLeftLines( layoutParameters,
665 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
666 const Vector<LineRun>& lines,
667 Vector<Vector2>& glyphPositions )
669 mImpl->Align( layoutParameters,
676 } // namespace Toolkit