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 mAlignment( LayoutEngine::ALIGN_BEGIN )
60 mFontClient = TextAbstraction::FontClient::Get();
64 * Retrieves the line layout for a given box width.
66 void GetLineLayoutForBox( const LayoutParameters& parameters,
67 LineLayout& lineLayout )
69 // Initializes the line layout.
70 lineLayout.numberOfCharacters = 0u;
71 lineLayout.numberOfGlyphs = 0u;
72 lineLayout.length = 0.f;
73 lineLayout.wsLengthEndOfLine = 0.f;
74 lineLayout.ascender = 0.f;
75 lineLayout.descender = 0.f;
77 // Get the last glyph index.
78 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
80 FontId lastFontId = 0u;
81 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
82 glyphIndex < parameters.totalNumberOfGlyphs;
85 // Get the glyph info.
86 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
88 // Check whether is a white space.
89 const Character character = *( parameters.textBuffer + lineLayout.numberOfCharacters );
90 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
92 // Get the character indices for the current glyph. The last character index is needed
93 // because there are glyphs formed by more than one character but their break info is
94 // given only for the last character.
95 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
97 // Increase the number of characters.
98 lineLayout.numberOfCharacters += charactersPerGlyph;
100 // Increase the number of glyphs.
101 lineLayout.numberOfGlyphs++;
103 // Increase the accumulated length.
104 const float glyphLength = ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
108 // Add the length to the length of white spaces at the end of the line.
109 lineLayout.wsLengthEndOfLine += glyphLength;
113 // Add as well any previous white space length.
114 lineLayout.length += lineLayout.wsLengthEndOfLine + glyphLength;
116 // Clear the white space length at the end of the line.
117 lineLayout.wsLengthEndOfLine = 0.f;
120 if( lastFontId != glyphInfo.fontId )
122 Text::FontMetrics fontMetrics;
123 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
125 // Sets the maximum ascender.
126 if( fontMetrics.ascender > lineLayout.ascender )
128 lineLayout.ascender = fontMetrics.ascender;
131 // Sets the maximum descender.
132 if( fontMetrics.descender > lineLayout.descender )
134 lineLayout.descender = fontMetrics.descender;
137 lastFontId = glyphInfo.fontId;
143 * Retrieves the line layout for a given box width.
145 void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
146 LineLayout& lineLayout )
148 // Initializes the line layout.
149 lineLayout.numberOfCharacters = 0u;
150 lineLayout.numberOfGlyphs = 0u;
151 lineLayout.length = 0.f;
152 lineLayout.widthAdvanceDiff = 0.f;
153 lineLayout.wsLengthEndOfLine = 0.f;
154 lineLayout.ascender = 0.f;
155 lineLayout.descender = 0.f;
157 // Stores temporary line layout which has not been added to the final line layout.
158 LineLayout tmpLineLayout;
159 tmpLineLayout.numberOfCharacters = 0u;
160 tmpLineLayout.numberOfGlyphs = 0u;
161 tmpLineLayout.length = 0.f;
162 tmpLineLayout.widthAdvanceDiff = 0.f;
163 tmpLineLayout.wsLengthEndOfLine = 0.f;
164 tmpLineLayout.ascender = 0.f;
165 tmpLineLayout.descender = 0.f;
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.
203 tmpLineLayout.widthAdvanceDiff = 0.f;
207 // Add as well any previous white space length.
208 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
209 tmpLineLayout.widthAdvanceDiff = glyphInfo.width - glyphInfo.advance;
211 // Clear the white space length at the end of the line.
212 tmpLineLayout.wsLengthEndOfLine = 0.f;
215 // Check if the accumulated length fits in the width of the box.
216 if( lineLayout.length + tmpLineLayout.length + tmpLineLayout.widthAdvanceDiff + ( ( 0.f < tmpLineLayout.length ) ? lineLayout.wsLengthEndOfLine : 0.f ) > parameters.boundingBox.width )
218 // Current word does not fit in the box's width.
222 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
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;
228 lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
230 if( 0.f < tmpLineLayout.length )
232 lineLayout.length += lineLayout.wsLengthEndOfLine;
234 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
238 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
241 if( tmpLineLayout.ascender > lineLayout.ascender )
243 lineLayout.ascender = tmpLineLayout.ascender;
246 if( tmpLineLayout.descender > lineLayout.descender )
248 lineLayout.descender = tmpLineLayout.descender;
251 tmpLineLayout.numberOfCharacters = 0u;
252 tmpLineLayout.numberOfGlyphs = 0u;
253 tmpLineLayout.length = 0u;
254 tmpLineLayout.widthAdvanceDiff = 0u;
255 tmpLineLayout.wsLengthEndOfLine = 0u;
256 tmpLineLayout.ascender = 0.f;
257 tmpLineLayout.descender = 0.f;
261 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
263 // Current glyph is the last one of the current word.
264 // Add the temporal layout to the current one.
265 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
266 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
267 lineLayout.length += tmpLineLayout.length;
268 lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
270 if( 0.f < tmpLineLayout.length )
272 lineLayout.length += lineLayout.wsLengthEndOfLine;
274 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
278 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
281 if( tmpLineLayout.ascender > lineLayout.ascender )
283 lineLayout.ascender = tmpLineLayout.ascender;
286 if( tmpLineLayout.descender > lineLayout.descender )
288 lineLayout.descender = tmpLineLayout.descender;
291 tmpLineLayout.numberOfCharacters = 0u;
292 tmpLineLayout.numberOfGlyphs = 0u;
293 tmpLineLayout.length = 0u;
294 tmpLineLayout.widthAdvanceDiff = 0u;
295 tmpLineLayout.wsLengthEndOfLine = 0u;
296 tmpLineLayout.ascender = 0.f;
297 tmpLineLayout.descender = 0.f;
300 if( lastFontId != glyphInfo.fontId )
302 Text::FontMetrics fontMetrics;
303 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
305 // Sets the maximum ascender.
306 if( fontMetrics.ascender > tmpLineLayout.ascender )
308 tmpLineLayout.ascender = fontMetrics.ascender;
311 // Sets the maximum descender.
312 if( -fontMetrics.descender > tmpLineLayout.descender )
314 tmpLineLayout.descender = -fontMetrics.descender;
317 lastFontId = glyphInfo.fontId;
322 bool LayoutText( const LayoutParameters& layoutParameters,
323 Vector<Vector2>& glyphPositions,
324 Vector<LineRun>& lines,
327 // TODO Switch between different layouts
332 case LayoutEngine::SINGLE_LINE_BOX:
334 update = SingleLineLayout( layoutParameters,
340 case LayoutEngine::MULTI_LINE_BOX:
342 update = MultiLineLayout( layoutParameters,
355 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
356 Vector<Vector2>& glyphPositions )
358 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
360 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer +lineIndex );
364 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
366 for( CharacterIndex characterLogicalIndex = 0u;
367 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
368 ++characterLogicalIndex )
370 // Convert the character in the logical order into the character in the visual order.
371 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
373 // Get the number of glyphs of the character.
374 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
376 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
378 // Convert the character in the visual order into the glyph in the visual order.
379 GlyphIndex glyphIndex = 1u + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex + index ) - numberOfGlyphs;
381 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
382 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
384 position.x = penX + glyph.xBearing;
385 penX += glyph.advance;
391 void Align( const LayoutParameters& layoutParameters,
392 const Size& layoutSize,
393 const Vector<LineRun>& lines,
394 Vector<Vector2>& glyphPositions )
396 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
398 // Traverse all lines and align the glyphs.
399 // LayoutParameters contains bidirectional info for those lines with
400 // right to left text, this info includes the paragraph's direction.
402 LineIndex bidiLineIndex = 0u;
403 for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
407 const LineRun& line = *it;
409 // 1) Get the paragrap's direction.
410 bool paragraphDirection = false;
412 // Check if there is any right to left line.
413 if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
414 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
416 const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
418 // Get the right to left line that match with current line.
419 while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
420 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
423 bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
426 if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
428 paragraphDirection = bidiLine->direction;
432 // 2) Calculate the alignment offset accordingly with the align option,
433 // the box width, line length, and the paragraphs direction.
434 float alignOffset = CalculateAlignment( layoutSize.width,
437 paragraphDirection );
439 // 3) Traverse all glyphs and update the 'x' position.
440 for( GlyphIndex index = line.glyphIndex,
441 endIndex = line.glyphIndex + line.numberOfGlyphs;
445 Vector2& position = *( glyphPositionsBuffer + index );
447 position.x += alignOffset;
452 bool SingleLineLayout( const LayoutParameters& layoutParameters,
453 Vector<Vector2>& glyphPositions,
454 Vector<LineRun>& lines,
458 layout.glyphIndex = 0u;
459 GetLineLayoutForBox( layoutParameters,
462 // Create a line run and add it to the lines.
463 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
466 lineRun.glyphIndex = 0u;
467 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
468 lineRun.characterRun.characterIndex = 0u;
469 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
470 lineRun.lineSize.width = layout.length;
471 lineRun.lineSize.height = layout.ascender + layout.descender;
472 lineRun.extraLength = layout.wsLengthEndOfLine;
474 lines.PushBack( lineRun );
476 // Update the actual size.
477 actualSize.width = layout.length;
478 actualSize.height = lineRun.lineSize.height;
481 float penY = layout.ascender;
483 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
484 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
486 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
487 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
489 position.x = penX + glyph.xBearing;
490 position.y = penY - glyph.yBearing;
492 penX += glyph.advance;
498 bool MultiLineLayout( const LayoutParameters& layoutParameters,
499 Vector<Vector2>& glyphPositions,
500 Vector<LineRun>& lines,
504 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
508 // Get the layout for the line.
510 layout.glyphIndex = index;
511 GetMultiLineLayoutForBox( layoutParameters,
514 if( 0u == layout.numberOfGlyphs )
516 // The width is too small and no characters are laid-out.
520 // Create a line run and add it to the lines.
521 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
524 lineRun.glyphIndex = index;
525 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
526 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
527 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
528 lineRun.lineSize.width = layout.length + ( ( layout.widthAdvanceDiff > 0.f ) ? layout.widthAdvanceDiff : 0.f );
529 lineRun.lineSize.height = layout.ascender + layout.descender;
530 lineRun.extraLength = layout.wsLengthEndOfLine;
532 lines.PushBack( lineRun );
534 // Update the actual size.
535 if( layout.length + layout.widthAdvanceDiff > actualSize.width )
537 actualSize.width = layout.length;
540 actualSize.height += lineRun.lineSize.height;
542 // Traverse the glyphs and set the positions.
544 penY += layout.ascender;
546 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
547 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
549 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
550 Vector2& position = *( glyphPositionsBuffer + i );
552 position.x = penX + glyph.xBearing;
553 position.y = penY - glyph.yBearing;
555 penX += glyph.advance;
558 penY += layout.descender;
560 // Increase the glyph index.
561 index += layout.numberOfGlyphs;
567 float CalculateAlignment( float boxWidth,
570 bool paragraphDirection )
574 Alignment alignment = mAlignment;
575 if( paragraphDirection &&
576 ( ALIGN_CENTER != alignment ) )
578 if( ALIGN_BEGIN == alignment )
580 alignment = ALIGN_END;
584 alignment = ALIGN_BEGIN;
597 offset = 0.5f * ( boxWidth - lineLength );
598 const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
599 offset = static_cast<float>( intOffset );
604 offset = boxWidth - lineLength;
609 if( paragraphDirection )
611 offset -= extraLength;
617 LayoutEngine::Layout mLayout;
618 LayoutEngine::Alignment mAlignment;
620 TextAbstraction::FontClient mFontClient;
623 LayoutEngine::LayoutEngine()
626 mImpl = new LayoutEngine::Impl();
629 LayoutEngine::~LayoutEngine()
634 void LayoutEngine::SetLayout( Layout layout )
636 mImpl->mLayout = layout;
639 unsigned int LayoutEngine::GetLayout() const
641 return mImpl->mLayout;
644 void LayoutEngine::SetAlignment( Alignment alignment )
646 mImpl->mAlignment = alignment;
649 LayoutEngine::Alignment LayoutEngine::GetAlignment() const
651 return mImpl->mAlignment;
654 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
655 Vector<Vector2>& glyphPositions,
656 Vector<LineRun>& lines,
659 return mImpl->LayoutText( layoutParameters,
665 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
666 Vector<Vector2>& glyphPositions )
668 mImpl->ReLayoutRightToLeftLines( layoutParameters,
672 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
673 const Size& layoutSize,
674 const Vector<LineRun>& lines,
675 Vector<Vector2>& glyphPositions )
677 mImpl->Align( layoutParameters,
685 } // namespace Toolkit