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>
23 #include <dali/public-api/math/vector2.h>
24 #include <dali/public-api/text-abstraction/font-client.h>
27 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
28 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
42 const float MAX_FLOAT = std::numeric_limits<float>::max();
47 * @brief Stores temporary layout info of the line.
54 numberOfCharacters( 0u ),
57 widthAdvanceDiff( 0.f ),
58 wsLengthEndOfLine( 0.f ),
60 descender( MAX_FLOAT )
70 numberOfCharacters = 0u;
73 widthAdvanceDiff = 0.f;
74 wsLengthEndOfLine = 0.f;
76 descender = MAX_FLOAT;
79 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
80 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
81 Length numberOfCharacters; ///< The number of characters which fit in one line.
82 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
83 float length; ///< The length of the glyphs which fit in one line.
84 float widthAdvanceDiff; ///< The difference between the width and the advance of the last glyph.
85 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
86 float ascender; ///< The maximum ascender of all fonts in the line.
87 float descender; ///< The minimum descender of all fonts in the line.
90 struct LayoutEngine::Impl
93 : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
94 mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
95 mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP )
97 mFontClient = TextAbstraction::FontClient::Get();
101 * @brief Merges a temporary line layout into the line layout.
103 * @param[in,out] lineLayout The line layout.
104 * @param[in] tmpLineLayout A temporary line layout.
106 void MergeLineLayout( LineLayout& lineLayout,
107 const LineLayout& tmpLineLayout )
109 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
110 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
111 lineLayout.length += tmpLineLayout.length;
112 lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
114 if( 0.f < tmpLineLayout.length )
116 lineLayout.length += lineLayout.wsLengthEndOfLine;
118 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
122 lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
125 if( tmpLineLayout.ascender > lineLayout.ascender )
127 lineLayout.ascender = tmpLineLayout.ascender;
130 if( tmpLineLayout.descender < lineLayout.descender )
132 lineLayout.descender = tmpLineLayout.descender;
137 * Retrieves the line layout for a given box width.
139 void GetLineLayoutForBox( const LayoutParameters& parameters,
140 LineLayout& lineLayout )
142 // Stores temporary line layout which has not been added to the final line layout.
143 LineLayout tmpLineLayout;
145 const bool isMultiline = mLayout == MULTI_LINE_BOX;
146 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
148 FontId lastFontId = 0u;
149 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
150 glyphIndex < parameters.totalNumberOfGlyphs;
153 const bool isLastGlyph = glyphIndex == lastGlyphIndex;
155 // Get the glyph info.
156 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
158 // Get the character indices for the current glyph. The last character index is needed
159 // because there are glyphs formed by more than one character but their break info is
160 // given only for the last character.
161 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
162 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
163 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
165 // Get the line break info for the current character.
166 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
168 // Get the word break info for the current character.
169 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
171 // Increase the number of characters.
172 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
174 // Increase the number of glyphs.
175 tmpLineLayout.numberOfGlyphs++;
177 // Check whether is a white space.
178 const Character character = *( parameters.textBuffer + characterFirstIndex );
179 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
181 // Increase the accumulated length.
184 // Add the length to the length of white spaces at the end of the line.
185 tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // I use the advance as the width is always zero for the white spaces.
186 tmpLineLayout.widthAdvanceDiff = 0.f;
190 // Add as well any previous white space length.
191 tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
192 tmpLineLayout.widthAdvanceDiff = glyphInfo.width - glyphInfo.advance;
194 // Clear the white space length at the end of the line.
195 tmpLineLayout.wsLengthEndOfLine = 0.f;
198 // Check if the accumulated length fits in the width of the box.
200 ( lineLayout.length + tmpLineLayout.length + tmpLineLayout.widthAdvanceDiff + ( ( 0.f < tmpLineLayout.length ) ? lineLayout.wsLengthEndOfLine : 0.f ) > parameters.boundingBox.width ) )
202 // Current word does not fit in the box's width.
206 if( ( isMultiline || isLastGlyph ) &&
207 ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
209 // Must break the line. Update the line layout and return.
210 MergeLineLayout( lineLayout, tmpLineLayout );
216 ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
218 // Current glyph is the last one of the current word.
219 // Add the temporal layout to the current one.
220 MergeLineLayout( lineLayout, tmpLineLayout );
222 tmpLineLayout.Clear();
225 if( lastFontId != glyphInfo.fontId )
227 Text::FontMetrics fontMetrics;
228 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
230 // Sets the maximum ascender.
231 if( fontMetrics.ascender > tmpLineLayout.ascender )
233 tmpLineLayout.ascender = fontMetrics.ascender;
236 // Sets the minimum descender.
237 if( -fontMetrics.descender < tmpLineLayout.descender )
239 tmpLineLayout.descender = fontMetrics.descender;
242 lastFontId = glyphInfo.fontId;
247 bool LayoutText( const LayoutParameters& layoutParameters,
248 Vector<Vector2>& glyphPositions,
249 Vector<LineRun>& lines,
253 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
257 // Get the layout for the line.
259 layout.glyphIndex = index;
260 GetLineLayoutForBox( layoutParameters,
263 if( 0u == layout.numberOfGlyphs )
265 // The width is too small and no characters are laid-out.
269 // Create a line run and add it to the lines.
270 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
273 lineRun.glyphIndex = index;
274 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
275 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
276 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
277 lineRun.width = layout.length + ( ( layout.widthAdvanceDiff > 0.f ) ? layout.widthAdvanceDiff : 0.f );
278 lineRun.ascender = layout.ascender;
279 lineRun.descender = layout.descender;
280 lineRun.extraLength = layout.wsLengthEndOfLine;
281 lineRun.direction = false;
283 lines.PushBack( lineRun );
285 // Update the actual size.
286 if( layout.length + layout.widthAdvanceDiff > actualSize.width )
288 actualSize.width = layout.length;
291 actualSize.height += ( lineRun.ascender + -lineRun.descender );
293 // Traverse the glyphs and set the positions.
295 penY += layout.ascender;
297 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
298 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
300 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
301 Vector2& position = *( glyphPositionsBuffer + i );
303 position.x = penX + glyph.xBearing;
304 position.y = penY - glyph.yBearing;
306 penX += glyph.advance;
309 penY += -layout.descender;
311 // Increase the glyph index.
312 index += layout.numberOfGlyphs;
318 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
319 Vector<Vector2>& glyphPositions )
321 // Traverses the paragraphs with right to left characters.
322 for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
324 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
328 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
330 // Traverses the characters of the right to left paragraph.
331 for( CharacterIndex characterLogicalIndex = 0u;
332 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
333 ++characterLogicalIndex )
335 // Convert the character in the logical order into the character in the visual order.
336 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
338 // Get the number of glyphs of the character.
339 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
341 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
343 // Convert the character in the visual order into the glyph in the visual order.
344 const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
346 DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
348 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
349 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
351 position.x = penX + glyph.xBearing;
352 penX += glyph.advance;
358 void Align( const LayoutParameters& layoutParameters,
359 const Size& layoutSize,
360 const Vector<LineRun>& lines,
361 Vector<Vector2>& glyphPositions )
363 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
365 // Traverse all lines and align the glyphs.
366 // LayoutParameters contains bidirectional info for those lines with
367 // right to left text, this info includes the paragraph's direction.
369 LineIndex bidiLineIndex = 0u;
370 for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
374 const LineRun& line = *it;
376 // 1) Get the paragrap's direction.
377 bool paragraphDirection = false;
379 // Check if there is any right to left line.
380 if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
381 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
383 const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
385 // Get the right to left line that match with current line.
386 while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
387 ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
390 bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
393 if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
395 paragraphDirection = bidiLine->direction;
399 // 2) Calculate the alignment offset accordingly with the align option,
400 // the box width, line length, and the paragraphs direction.
401 float alignOffset = CalculateHorizontalAlignment( layoutSize.width,
404 paragraphDirection );
406 // 3) Traverse all glyphs and update the 'x' position.
407 for( GlyphIndex index = line.glyphIndex,
408 endIndex = line.glyphIndex + line.numberOfGlyphs;
412 Vector2& position = *( glyphPositionsBuffer + index );
414 position.x += alignOffset;
419 float CalculateHorizontalAlignment( float boxWidth,
422 bool paragraphDirection )
426 HorizontalAlignment alignment = mHorizontalAlignment;
427 if( paragraphDirection &&
428 ( HORIZONTAL_ALIGN_CENTER != alignment ) )
430 if( HORIZONTAL_ALIGN_BEGIN == alignment )
432 alignment = HORIZONTAL_ALIGN_END;
436 alignment = HORIZONTAL_ALIGN_BEGIN;
442 case HORIZONTAL_ALIGN_BEGIN:
447 case HORIZONTAL_ALIGN_CENTER:
449 offset = 0.5f * ( boxWidth - lineLength );
450 const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
451 offset = static_cast<float>( intOffset );
454 case HORIZONTAL_ALIGN_END:
456 offset = boxWidth - lineLength;
461 if( paragraphDirection )
463 offset -= extraLength;
469 LayoutEngine::Layout mLayout;
470 LayoutEngine::HorizontalAlignment mHorizontalAlignment;
471 LayoutEngine::VerticalAlignment mVerticalAlignment;
473 TextAbstraction::FontClient mFontClient;
476 LayoutEngine::LayoutEngine()
479 mImpl = new LayoutEngine::Impl();
482 LayoutEngine::~LayoutEngine()
487 void LayoutEngine::SetLayout( Layout layout )
489 mImpl->mLayout = layout;
492 unsigned int LayoutEngine::GetLayout() const
494 return mImpl->mLayout;
497 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
499 mImpl->mHorizontalAlignment = alignment;
502 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
504 return mImpl->mHorizontalAlignment;
507 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
509 mImpl->mVerticalAlignment = alignment;
512 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
514 return mImpl->mVerticalAlignment;
517 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
518 Vector<Vector2>& glyphPositions,
519 Vector<LineRun>& lines,
522 return mImpl->LayoutText( layoutParameters,
528 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
529 Vector<Vector2>& glyphPositions )
531 mImpl->ReLayoutRightToLeftLines( layoutParameters,
535 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
536 const Size& layoutSize,
537 const Vector<LineRun>& lines,
538 Vector<Vector2>& glyphPositions )
540 mImpl->Align( layoutParameters,
548 } // namespace Toolkit