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 // Traverses the paragraphs with right to left characters.
359 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
361 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
365 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
367 // Traverses the characters of the right to left paragraph.
368 for( CharacterIndex characterLogicalIndex = 0u;
369 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
370 ++characterLogicalIndex )
372 // Convert the character in the logical order into the character in the visual order.
373 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
375 // Get the number of glyphs of the character.
376 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
378 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
380 // Convert the character in the visual order into the glyph in the visual order.
381 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
383 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
385 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
386 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
388 position.x = penX + glyph.xBearing;
389 penX += glyph.advance;
395 void Align( const LayoutParameters& layoutParameters,
396 const Size& layoutSize,
397 const Vector<LineRun>& lines,
398 Vector<Vector2>& glyphPositions )
400 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
402 // Traverse all lines and align the glyphs.
403 // LayoutParameters contains bidirectional info for those lines with
404 // right to left text, this info includes the paragraph's direction.
406 LineIndex bidiLineIndex = 0u;
407 for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
411 const LineRun& line = *it;
413 // 1) Get the paragrap's direction.
414 bool paragraphDirection = false;
416 // Check if there is any right to left line.
417 if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
418 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
420 const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
422 // Get the right to left line that match with current line.
423 while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
424 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
427 bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
430 if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
432 paragraphDirection = bidiLine->direction;
436 // 2) Calculate the alignment offset accordingly with the align option,
437 // the box width, line length, and the paragraphs direction.
438 float alignOffset = CalculateAlignment( layoutSize.width,
441 paragraphDirection );
443 // 3) Traverse all glyphs and update the 'x' position.
444 for( GlyphIndex index = line.glyphIndex,
445 endIndex = line.glyphIndex + line.numberOfGlyphs;
449 Vector2& position = *( glyphPositionsBuffer + index );
451 position.x += alignOffset;
456 bool SingleLineLayout( const LayoutParameters& layoutParameters,
457 Vector<Vector2>& glyphPositions,
458 Vector<LineRun>& lines,
462 layout.glyphIndex = 0u;
463 GetLineLayoutForBox( layoutParameters,
466 // Create a line run and add it to the lines.
467 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
470 lineRun.glyphIndex = 0u;
471 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
472 lineRun.characterRun.characterIndex = 0u;
473 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
474 lineRun.lineSize.width = layout.length;
475 lineRun.lineSize.height = layout.ascender + layout.descender;
476 lineRun.extraLength = layout.wsLengthEndOfLine;
478 lines.PushBack( lineRun );
480 // Update the actual size.
481 actualSize.width = layout.length;
482 actualSize.height = lineRun.lineSize.height;
485 float penY = layout.ascender;
487 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
488 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
490 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
491 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
493 position.x = penX + glyph.xBearing;
494 position.y = penY - glyph.yBearing;
496 penX += glyph.advance;
502 bool MultiLineLayout( const LayoutParameters& layoutParameters,
503 Vector<Vector2>& glyphPositions,
504 Vector<LineRun>& lines,
508 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
512 // Get the layout for the line.
514 layout.glyphIndex = index;
515 GetMultiLineLayoutForBox( layoutParameters,
518 if( 0u == layout.numberOfGlyphs )
520 // The width is too small and no characters are laid-out.
524 // Create a line run and add it to the lines.
525 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
528 lineRun.glyphIndex = index;
529 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
530 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
531 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
532 lineRun.lineSize.width = layout.length + ( ( layout.widthAdvanceDiff > 0.f ) ? layout.widthAdvanceDiff : 0.f );
533 lineRun.lineSize.height = layout.ascender + layout.descender;
534 lineRun.extraLength = layout.wsLengthEndOfLine;
536 lines.PushBack( lineRun );
538 // Update the actual size.
539 if( layout.length + layout.widthAdvanceDiff > actualSize.width )
541 actualSize.width = layout.length;
544 actualSize.height += lineRun.lineSize.height;
546 // Traverse the glyphs and set the positions.
548 penY += layout.ascender;
550 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
551 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
553 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
554 Vector2& position = *( glyphPositionsBuffer + i );
556 position.x = penX + glyph.xBearing;
557 position.y = penY - glyph.yBearing;
559 penX += glyph.advance;
562 penY += layout.descender;
564 // Increase the glyph index.
565 index += layout.numberOfGlyphs;
571 float CalculateAlignment( float boxWidth,
574 bool paragraphDirection )
578 Alignment alignment = mAlignment;
579 if( paragraphDirection &&
580 ( ALIGN_CENTER != alignment ) )
582 if( ALIGN_BEGIN == alignment )
584 alignment = ALIGN_END;
588 alignment = ALIGN_BEGIN;
601 offset = 0.5f * ( boxWidth - lineLength );
602 const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
603 offset = static_cast<float>( intOffset );
608 offset = boxWidth - lineLength;
613 if( paragraphDirection )
615 offset -= extraLength;
621 LayoutEngine::Layout mLayout;
622 LayoutEngine::Alignment mAlignment;
624 TextAbstraction::FontClient mFontClient;
627 LayoutEngine::LayoutEngine()
630 mImpl = new LayoutEngine::Impl();
633 LayoutEngine::~LayoutEngine()
638 void LayoutEngine::SetLayout( Layout layout )
640 mImpl->mLayout = layout;
643 unsigned int LayoutEngine::GetLayout() const
645 return mImpl->mLayout;
648 void LayoutEngine::SetAlignment( Alignment alignment )
650 mImpl->mAlignment = alignment;
653 LayoutEngine::Alignment LayoutEngine::GetAlignment() const
655 return mImpl->mAlignment;
658 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
659 Vector<Vector2>& glyphPositions,
660 Vector<LineRun>& lines,
663 return mImpl->LayoutText( layoutParameters,
669 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
670 Vector<Vector2>& glyphPositions )
672 mImpl->ReLayoutRightToLeftLines( layoutParameters,
676 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
677 const Size& layoutSize,
678 const Vector<LineRun>& lines,
679 Vector<Vector2>& glyphPositions )
681 mImpl->Align( layoutParameters,
689 } // namespace Toolkit