Merge "Text cleaning." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
20
21 // EXTERNAL INCLUDES
22 #include <limits>
23 #include <cmath>
24 #include <dali/integration-api/debug.h>
25 #include <dali/devel-api/text-abstraction/font-client.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Text
40 {
41
42 namespace Layout
43 {
44
45 namespace
46 {
47
48 #if defined(DEBUG_ENABLED)
49   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
50 #endif
51
52 const float MAX_FLOAT = std::numeric_limits<float>::max();
53 const CharacterDirection LTR = false;
54 const CharacterDirection RTL = !LTR;
55 const float LINE_SPACING= 0.f;
56
57 inline bool isEmptyLineAtLast( const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line )
58 {
59   return ( (*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End() );
60 }
61
62 } //namespace
63
64 /**
65  * @brief Stores temporary layout info of the line.
66  */
67 struct LineLayout
68 {
69   LineLayout()
70   : glyphIndex( 0u ),
71     characterIndex( 0u ),
72     numberOfGlyphs( 0u ),
73     numberOfCharacters( 0u ),
74     ascender( -MAX_FLOAT ),
75     descender( MAX_FLOAT ),
76     lineSpacing( 0.f ),
77     penX( 0.f ),
78     previousAdvance( 0.f ),
79     length( 0.f ),
80     wsLengthEndOfLine( 0.f )
81   {}
82
83   ~LineLayout()
84   {}
85
86   void Clear()
87   {
88     glyphIndex = 0u;
89     characterIndex = 0u;
90     numberOfGlyphs = 0u;
91     numberOfCharacters = 0u;
92     ascender = 0.f;
93     descender = MAX_FLOAT;
94   }
95
96   GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
97   CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
98   Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
99   Length         numberOfCharacters; ///< The number of characters which fit in one line.
100   float          ascender;           ///< The maximum ascender of all fonts in the line.
101   float          descender;          ///< The minimum descender of all fonts in the line.
102   float          lineSpacing;        ///< The line spacing
103   float          penX;               ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
104   float          previousAdvance;    ///< The advance of the previous glyph.
105   float          length;             ///< The current length of the line.
106   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
107 };
108
109 struct Engine::Impl
110 {
111   Impl()
112   : mLayout( Layout::Engine::SINGLE_LINE_BOX ),
113     mCursorWidth( 0.f ),
114     mDefaultLineSpacing( LINE_SPACING )
115   {
116   }
117
118   /**
119    * @brief Updates the line ascender and descender with the metrics of a new font.
120    *
121    * @param[in] glyphMetrics The metrics of the new font.
122    * @param[in,out] lineLayout The line layout.
123    */
124   void UpdateLineHeight( const GlyphMetrics& glyphMetrics, LineLayout& lineLayout )
125   {
126     Text::FontMetrics fontMetrics;
127     if( 0u != glyphMetrics.fontId )
128     {
129       mMetrics->GetFontMetrics( glyphMetrics.fontId, fontMetrics );
130     }
131     else
132     {
133       fontMetrics.ascender = glyphMetrics.fontHeight;
134       fontMetrics.descender = 0.f;
135       fontMetrics.height = fontMetrics.ascender;
136       fontMetrics.underlinePosition = 0.f;
137       fontMetrics.underlineThickness = 1.f;
138     }
139
140     // Sets the maximum ascender.
141     lineLayout.ascender = std::max( lineLayout.ascender, fontMetrics.ascender );
142
143     // Sets the minimum descender.
144     lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender );
145
146     // set the line spacing
147     lineLayout.lineSpacing = mDefaultLineSpacing;
148   }
149
150   /**
151    * @brief Merges a temporary line layout into the line layout.
152    *
153    * @param[in,out] lineLayout The line layout.
154    * @param[in] tmpLineLayout A temporary line layout.
155    */
156   void MergeLineLayout( LineLayout& lineLayout,
157                         const LineLayout& tmpLineLayout )
158   {
159     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
160     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
161
162     lineLayout.penX = tmpLineLayout.penX;
163     lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
164
165     lineLayout.length = tmpLineLayout.length;
166     lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
167
168     // Sets the maximum ascender.
169     lineLayout.ascender = std::max( lineLayout.ascender, tmpLineLayout.ascender );
170
171     // Sets the minimum descender.
172     lineLayout.descender = std::min( lineLayout.descender, tmpLineLayout.descender );
173   }
174
175   /**
176    * Retrieves the line layout for a given box width.
177    *
178    * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
179    *       because the number of characters of the line is not known (one of the responsabilities of this method
180    *       is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
181    *       and left to right text is laid-out, it can be small differences in the line length. One solution is to
182    *       reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
183    *       this method calculates which are the first and last glyphs of the line (the ones that causes the
184    *       differences). This is a good point to check if there is problems with the text exceeding the boundaries
185    *       of the control when there is right to left text.
186    *
187    * @param[in] parameters The layout parameters.
188    * @param[out] lineLayout The line layout.
189    * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
190    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
191    */
192   void GetLineLayoutForBox( const Parameters& parameters,
193                             LineLayout& lineLayout,
194                             CharacterDirection& paragraphDirection,
195                             bool completelyFill )
196   {
197     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
198     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex );
199
200     const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
201     const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
202     const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
203     const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
204     const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
205
206     const bool hasBidiParagraphs = !parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
207     const CharacterDirection* const characterDirectionBuffer = hasBidiParagraphs ? parameters.textModel->mLogicalModel->mCharacterDirections.Begin() : nullptr;
208
209     const float outlineWidth = static_cast<float>( parameters.textModel->GetOutlineWidth() );
210     const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
211
212     const bool isMultiline = mLayout == MULTI_LINE_BOX;
213     const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD;
214
215     // The last glyph to be laid-out.
216     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
217
218     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
219     // In the case the line starts with a right to left character, if the width is longer than the advance,
220     // the difference needs to be added to the line length.
221
222     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
223     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
224                                                                    lastGlyphOfParagraphPlusOne,
225                                                                    charactersPerGlyphBuffer );
226
227     GlyphMetrics glyphMetrics;
228     GetGlyphsMetrics( lineLayout.glyphIndex,
229                       numberOfGLyphsInGroup,
230                       glyphMetrics,
231                       glyphsBuffer,
232                       mMetrics );
233
234     // Set the direction of the first character of the line.
235     lineLayout.characterIndex = *( glyphsToCharactersBuffer + lineLayout.glyphIndex );
236
237     // Stores temporary line layout which has not been added to the final line layout.
238     LineLayout tmpLineLayout;
239
240     // Initialize the start point.
241
242     // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
243     // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
244     // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined.
245     tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
246
247     // Initialize the advance of the previous glyph.
248     tmpLineLayout.previousAdvance = 0.f;
249
250     // Calculate the line height if there is no characters.
251     FontId lastFontId = glyphMetrics.fontId;
252     UpdateLineHeight( glyphMetrics, tmpLineLayout );
253
254     bool oneWordLaidOut = false;
255
256     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
257          glyphIndex < lastGlyphOfParagraphPlusOne; )
258     {
259       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  glyph index : %d\n", glyphIndex );
260
261       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
262       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
263                                                                      lastGlyphOfParagraphPlusOne,
264                                                                      charactersPerGlyphBuffer );
265
266       GlyphMetrics glyphMetrics;
267       GetGlyphsMetrics( glyphIndex,
268                         numberOfGLyphsInGroup,
269                         glyphMetrics,
270                         glyphsBuffer,
271                         mMetrics );
272
273       const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup  == totalNumberOfGlyphs;
274
275       // Check if the font of the current glyph is the same of the previous one.
276       // If it's different the ascender and descender need to be updated.
277       if( lastFontId != glyphMetrics.fontId )
278       {
279         UpdateLineHeight( glyphMetrics, tmpLineLayout );
280         lastFontId = glyphMetrics.fontId;
281       }
282
283       // Get the character indices for the current glyph. The last character index is needed
284       // because there are glyphs formed by more than one character but their break info is
285       // given only for the last character.
286       const Length charactersPerGlyph = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
287       const bool hasCharacters = charactersPerGlyph > 0u;
288       const CharacterIndex characterFirstIndex = *( glyphsToCharactersBuffer + glyphIndex );
289       const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
290
291       // Get the line break info for the current character.
292       const LineBreakInfo lineBreakInfo = hasCharacters ? *( lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
293
294       // Increase the number of characters.
295       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
296
297       // Increase the number of glyphs.
298       tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
299
300       // Check whether is a white space.
301       const Character character = *( textBuffer + characterFirstIndex );
302       const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
303
304       // Calculate the length of the line.
305
306       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
307       const float previousTmpPenX = tmpLineLayout.penX;
308       const float previousTmpAdvance = tmpLineLayout.previousAdvance;
309       const float previousTmpLength = tmpLineLayout.length;
310       const float previousTmpWsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
311
312       if( isWhiteSpace )
313       {
314         // Add the length to the length of white spaces at the end of the line.
315         tmpLineLayout.wsLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
316       }
317       else
318       {
319         tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.wsLengthEndOfLine;
320         tmpLineLayout.previousAdvance = ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
321         tmpLineLayout.length = tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width;
322
323         // Clear the white space length at the end of the line.
324         tmpLineLayout.wsLengthEndOfLine = 0.f;
325       }
326
327       // Check if the accumulated length fits in the width of the box.
328       if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
329           ( tmpLineLayout.length > parameters.boundingBox.width ) )
330       {
331         // Current word does not fit in the box's width.
332         if( !oneWordLaidOut || completelyFill )
333         {
334           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Break the word by character\n" );
335
336           // The word doesn't fit in the control's width. It needs to be split by character.
337           if( tmpLineLayout.numberOfGlyphs > 0u )
338           {
339             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
340             tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
341
342             tmpLineLayout.penX = previousTmpPenX;
343             tmpLineLayout.previousAdvance = previousTmpAdvance;
344             tmpLineLayout.length = previousTmpLength;
345             tmpLineLayout.wsLengthEndOfLine = previousTmpWsLengthEndOfLine;
346           }
347
348           // Add part of the word to the line layout.
349           MergeLineLayout( lineLayout, tmpLineLayout );
350         }
351         else
352         {
353           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Current word does not fit.\n" );
354         }
355
356         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
357
358         return;
359       }
360
361       if( ( isMultiline || isLastGlyph ) &&
362           ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
363       {
364         // Must break the line. Update the line layout and return.
365         MergeLineLayout( lineLayout, tmpLineLayout );
366
367         // Set the next paragraph's direction.
368         if( !isLastGlyph &&
369             ( nullptr != characterDirectionBuffer ) )
370         {
371           paragraphDirection = *( characterDirectionBuffer + 1u + characterLastIndex );
372         }
373
374         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Must break\n" );
375         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
376
377         return;
378       }
379
380       if( isMultiline &&
381           ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
382       {
383         oneWordLaidOut = isWordLaidOut;
384         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  One word laid-out\n" );
385
386         // Current glyph is the last one of the current word.
387         // Add the temporal layout to the current one.
388         MergeLineLayout( lineLayout, tmpLineLayout );
389
390         tmpLineLayout.Clear();
391       }
392
393       glyphIndex += numberOfGLyphsInGroup;
394     }
395
396     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
397   }
398
399   void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
400                           Length numberOfGlyphs,
401                           float outlineWidth,
402                           float interGlyphExtraAdvance,
403                           Vector2* glyphPositionsBuffer )
404   {
405     // Traverse the glyphs and set the positions.
406
407     // Check if the x bearing of the first character is negative.
408     // If it has a negative x bearing, it will exceed the boundaries of the actor,
409     // so the penX position needs to be moved to the right.
410
411     const GlyphInfo& glyph = *glyphsBuffer;
412     float penX = -glyph.xBearing + mCursorWidth + outlineWidth;
413
414     for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
415     {
416       const GlyphInfo& glyph = *( glyphsBuffer + i );
417       Vector2& position = *( glyphPositionsBuffer + i );
418
419       position.x = std::roundf( penX + glyph.xBearing );
420       position.y = -glyph.yBearing;
421
422       penX += ( glyph.advance + interGlyphExtraAdvance );
423     }
424   }
425
426   /**
427    * @brief Resizes the line buffer.
428    *
429    * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
430    * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
431    * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
432    * @param[in] updateCurrentBuffer Whether the layout is updated.
433    *
434    * @return Pointer to either lines or newLines.
435    */
436   LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
437                               Vector<LineRun>& newLines,
438                               Length& linesCapacity,
439                               bool updateCurrentBuffer )
440   {
441     LineRun* linesBuffer = nullptr;
442     // Reserve more space for the next lines.
443     linesCapacity *= 2u;
444     if( updateCurrentBuffer )
445     {
446       newLines.Resize( linesCapacity );
447       linesBuffer = newLines.Begin();
448     }
449     else
450     {
451       lines.Resize( linesCapacity );
452       linesBuffer = lines.Begin();
453     }
454
455     return linesBuffer;
456   }
457
458   /**
459    * Ellipsis a line if it exceeds the width's of the bounding box.
460    *
461    * @param[in] layoutParameters The parameters needed to layout the text.
462    * @param[in] layout The line layout.
463    * @param[in,out] layoutSize The text's layout size.
464    * @param[in,out] linesBuffer Pointer to the line's buffer.
465    * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
466    * @param[in,out] numberOfLines The number of laid-out lines.
467    * @param[in] penY The vertical layout position.
468    * @param[in] currentParagraphDirection The current paragraph's direction.
469    * @param[in,out] isAutoScrollEnabled If the isAutoScrollEnabled is true and the height of the text exceeds the boundaries of the control the text is elided and the isAutoScrollEnabled is set to false to disable the autoscroll
470    *
471    * return Whether the line is ellipsized.
472    */
473   bool EllipsisLine( const Parameters& layoutParameters,
474                      const LineLayout& layout,
475                      Size& layoutSize,
476                      LineRun* linesBuffer,
477                      Vector2* glyphPositionsBuffer,
478                      Length& numberOfLines,
479                      float penY,
480                      CharacterDirection currentParagraphDirection,
481                      bool& isAutoScrollEnabled )
482   {
483     const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) :
484                                                 ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
485                                                   ( ( mLayout == SINGLE_LINE_BOX ) &&
486                                                     ( layout.length > layoutParameters.boundingBox.width ) ) );
487
488     if( ellipsis )
489     {
490       isAutoScrollEnabled = false;
491       // Do not layout more lines if ellipsis is enabled.
492
493       // The last line needs to be completely filled with characters.
494       // Part of a word may be used.
495
496       LineRun* lineRun = nullptr;
497       LineLayout ellipsisLayout;
498       if( 0u != numberOfLines )
499       {
500         // Get the last line and layout it again with the 'completelyFill' flag to true.
501         lineRun = linesBuffer + ( numberOfLines - 1u );
502
503         penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
504
505         ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
506       }
507       else
508       {
509         // At least there is space reserved for one line.
510         lineRun = linesBuffer;
511
512         lineRun->glyphRun.glyphIndex = 0u;
513         ellipsisLayout.glyphIndex = 0u;
514
515         ++numberOfLines;
516       }
517
518       GetLineLayoutForBox( layoutParameters,
519                            ellipsisLayout,
520                            currentParagraphDirection,
521                            true );
522
523       lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
524       lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
525       lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
526       lineRun->width = ellipsisLayout.length;
527       lineRun->extraLength = std::ceil( ellipsisLayout.wsLengthEndOfLine );
528       lineRun->ascender = ellipsisLayout.ascender;
529       lineRun->descender = ellipsisLayout.descender;
530       lineRun->direction = LTR;
531       lineRun->ellipsis = true;
532
533       layoutSize.width = layoutParameters.boundingBox.width;
534       if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
535       {
536         layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
537       }
538
539       const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
540       const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
541
542       SetGlyphPositions( glyphsBuffer + lineRun->glyphRun.glyphIndex,
543                          ellipsisLayout.numberOfGlyphs,
544                          outlineWidth,
545                          layoutParameters.interGlyphExtraAdvance,
546                          glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
547     }
548
549     return ellipsis;
550   }
551
552   /**
553    * @brief Updates the text layout with a new laid-out line.
554    *
555    * @param[in] layoutParameters The parameters needed to layout the text.
556    * @param[in] layout The line layout.
557    * @param[in,out] layoutSize The text's layout size.
558    * @param[in,out] linesBuffer Pointer to the line's buffer.
559    * @param[in] index Index to the vector of glyphs.
560    * @param[in,out] numberOfLines The number of laid-out lines.
561    * @param[in] isLastLine Whether the laid-out line is the last one.
562    */
563   void UpdateTextLayout( const Parameters& layoutParameters,
564                          const LineLayout& layout,
565                          Size& layoutSize,
566                          LineRun* linesBuffer,
567                          GlyphIndex index,
568                          Length& numberOfLines,
569                          bool isLastLine )
570   {
571     LineRun& lineRun = *( linesBuffer + numberOfLines );
572     ++numberOfLines;
573
574     lineRun.glyphRun.glyphIndex = index;
575     lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
576     lineRun.characterRun.characterIndex = layout.characterIndex;
577     lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
578     lineRun.lineSpacing = mDefaultLineSpacing;
579
580     if( isLastLine && !layoutParameters.isLastNewParagraph )
581     {
582       const float width = layout.length + layout.wsLengthEndOfLine;
583       if( MULTI_LINE_BOX == mLayout )
584       {
585         lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
586       }
587       else
588       {
589         lineRun.width = width;
590       }
591
592       lineRun.extraLength = 0.f;
593     }
594     else
595     {
596       lineRun.width = layout.length;
597       lineRun.extraLength = std::ceil( layout.wsLengthEndOfLine );
598     }
599
600     // Rounds upward to avoid a non integer size.
601     lineRun.width = std::ceil( lineRun.width );
602
603     lineRun.ascender = layout.ascender;
604     lineRun.descender = layout.descender;
605     lineRun.direction = LTR;
606     lineRun.ellipsis = false;
607
608     // Update the actual size.
609     if( lineRun.width > layoutSize.width )
610     {
611       layoutSize.width = lineRun.width;
612     }
613
614     layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
615   }
616
617   /**
618    * @brief Updates the text layout with the last laid-out line.
619    *
620    * @param[in] layoutParameters The parameters needed to layout the text.
621    * @param[in] characterIndex The character index of the line.
622    * @param[in] glyphIndex The glyph index of the line.
623    * @param[in,out] layoutSize The text's layout size.
624    * @param[in,out] linesBuffer Pointer to the line's buffer.
625    * @param[in,out] numberOfLines The number of laid-out lines.
626    */
627   void UpdateTextLayout( const Parameters& layoutParameters,
628                          CharacterIndex characterIndex,
629                          GlyphIndex glyphIndex,
630                          Size& layoutSize,
631                          LineRun* linesBuffer,
632                          Length& numberOfLines )
633   {
634     const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
635
636     // Need to add a new line with no characters but with height to increase the layoutSize.height
637     const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
638
639     Text::FontMetrics fontMetrics;
640     if( 0u != glyphInfo.fontId )
641     {
642       mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
643     }
644
645     LineRun& lineRun = *( linesBuffer + numberOfLines );
646     ++numberOfLines;
647
648     lineRun.glyphRun.glyphIndex = glyphIndex;
649     lineRun.glyphRun.numberOfGlyphs = 0u;
650     lineRun.characterRun.characterIndex = characterIndex;
651     lineRun.characterRun.numberOfCharacters = 0u;
652     lineRun.width = 0.f;
653     lineRun.ascender = fontMetrics.ascender;
654     lineRun.descender = fontMetrics.descender;
655     lineRun.extraLength = 0.f;
656     lineRun.alignmentOffset = 0.f;
657     lineRun.direction = LTR;
658     lineRun.ellipsis = false;
659     lineRun.lineSpacing = mDefaultLineSpacing;
660
661     layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
662   }
663
664   /**
665    * @brief Updates the text's layout size adding the size of the previously laid-out lines.
666    *
667    * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
668    * @param[in,out] layoutSize The text's layout size.
669    */
670   void UpdateLayoutSize( const Vector<LineRun>& lines,
671                          Size& layoutSize )
672   {
673     for( Vector<LineRun>::ConstIterator it = lines.Begin(),
674            endIt = lines.End();
675          it != endIt;
676          ++it )
677     {
678       const LineRun& line = *it;
679
680       if( line.width > layoutSize.width )
681       {
682         layoutSize.width = line.width;
683       }
684
685       layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
686     }
687   }
688
689   /**
690    * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
691    *
692    * @param[in] layoutParameters The parameters needed to layout the text.
693    * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
694    * @param[in] characterOffset The offset to be added to the runs of characters.
695    * @param[in] glyphOffset The offset to be added to the runs of glyphs.
696    */
697   void UpdateLineIndexOffsets( const Parameters& layoutParameters,
698                                Vector<LineRun>& lines,
699                                Length characterOffset,
700                                Length glyphOffset )
701   {
702     // Update the glyph and character runs.
703     for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
704            endIt = lines.End();
705          it != endIt;
706          ++it )
707     {
708       LineRun& line = *it;
709
710       line.glyphRun.glyphIndex = glyphOffset;
711       line.characterRun.characterIndex = characterOffset;
712
713       glyphOffset += line.glyphRun.numberOfGlyphs;
714       characterOffset += line.characterRun.numberOfCharacters;
715     }
716   }
717
718   bool LayoutText( const Parameters& layoutParameters,
719                    Vector<Vector2>& glyphPositions,
720                    Vector<LineRun>& lines,
721                    Size& layoutSize,
722                    bool elideTextEnabled,
723                    bool& isAutoScrollEnabled )
724   {
725     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
726     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
727
728     if( 0u == layoutParameters.numberOfGlyphs )
729     {
730       // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
731       if( layoutParameters.isLastNewParagraph )
732       {
733         Length numberOfLines = lines.Count();
734         if( 0u != numberOfLines )
735         {
736           const LineRun& lastLine = *( lines.End() - 1u );
737
738           if( 0u != lastLine.characterRun.numberOfCharacters )
739           {
740             // Need to add a new line with no characters but with height to increase the layoutSize.height
741             LineRun newLine;
742             Initialize( newLine );
743             lines.PushBack( newLine );
744
745             UpdateTextLayout( layoutParameters,
746                               lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
747                               lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
748                               layoutSize,
749                               lines.Begin(),
750                               numberOfLines );
751           }
752         }
753       }
754
755       // Calculates the layout size.
756       UpdateLayoutSize( lines,
757                         layoutSize );
758
759       // Rounds upward to avoid a non integer size.
760       layoutSize.height = std::ceil( layoutSize.height );
761
762       // Nothing else do if there are no glyphs to layout.
763       return false;
764     }
765
766     const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
767     const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
768
769     // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
770     // This extra line needs to be removed.
771     if( 0u != lines.Count() )
772     {
773       Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
774
775       if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
776           ( lastGlyphPlusOne == totalNumberOfGlyphs ) )
777       {
778         lines.Remove( lastLine );
779       }
780     }
781
782     // Retrieve BiDi info.
783     const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
784
785     const CharacterDirection* const characterDirectionBuffer = hasBidiParagraphs ? layoutParameters.textModel->mLogicalModel->mCharacterDirections.Begin() : nullptr;
786
787     // Set the first paragraph's direction.
788     CharacterDirection paragraphDirection = ( nullptr != characterDirectionBuffer ) ? *characterDirectionBuffer : LTR;
789
790     // Whether the layout is being updated or set from scratch.
791     const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
792
793     Vector2* glyphPositionsBuffer = nullptr;
794     Vector<Vector2> newGlyphPositions;
795
796     LineRun* linesBuffer = nullptr;
797     Vector<LineRun> newLines;
798
799     // Estimate the number of lines.
800     Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
801     Length numberOfLines = 0u;
802
803     if( updateCurrentBuffer )
804     {
805       newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
806       glyphPositionsBuffer = newGlyphPositions.Begin();
807
808       newLines.Resize( linesCapacity );
809       linesBuffer = newLines.Begin();
810     }
811     else
812     {
813       glyphPositionsBuffer = glyphPositions.Begin();
814
815       lines.Resize( linesCapacity );
816       linesBuffer = lines.Begin();
817     }
818
819
820     const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
821     const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
822
823     float penY = CalculateLineOffset( lines,
824                                       layoutParameters.startLineIndex );
825
826
827     for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
828     {
829       CharacterDirection currentParagraphDirection = paragraphDirection;
830
831       // Get the layout for the line.
832       LineLayout layout;
833       layout.glyphIndex = index;
834       GetLineLayoutForBox( layoutParameters,
835                            layout,
836                            paragraphDirection,
837                            false );
838
839       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex );
840       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex );
841       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs );
842       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters );
843       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                length %f\n", layout.length );
844
845       if( 0u == layout.numberOfGlyphs )
846       {
847         // The width is too small and no characters are laid-out.
848         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
849
850         lines.Resize( numberOfLines );
851
852         // Rounds upward to avoid a non integer size.
853         layoutSize.height = std::ceil( layoutSize.height );
854
855         return false;
856       }
857
858       // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
859       // of the box.
860       penY += layout.ascender;
861
862       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  pen y %f\n", penY );
863
864       bool ellipsis = false;
865       if( elideTextEnabled )
866       {
867         // Does the ellipsis of the last line.
868         ellipsis = EllipsisLine( layoutParameters,
869                                  layout,
870                                  layoutSize,
871                                  linesBuffer,
872                                  glyphPositionsBuffer,
873                                  numberOfLines,
874                                  penY,
875                                  currentParagraphDirection,
876                                  isAutoScrollEnabled );
877       }
878
879       if( ellipsis )
880       {
881         // No more lines to layout.
882         break;
883       }
884       else
885       {
886         // Whether the last line has been laid-out.
887         const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
888
889         if( numberOfLines == linesCapacity )
890         {
891           // Reserve more space for the next lines.
892           linesBuffer = ResizeLinesBuffer( lines,
893                                            newLines,
894                                            linesCapacity,
895                                            updateCurrentBuffer );
896         }
897
898         // Updates the current text's layout with the line's layout.
899         UpdateTextLayout( layoutParameters,
900                           layout,
901                           layoutSize,
902                           linesBuffer,
903                           index,
904                           numberOfLines,
905                           isLastLine );
906
907         const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
908
909         if( ( nextIndex == totalNumberOfGlyphs ) &&
910             layoutParameters.isLastNewParagraph &&
911             ( mLayout == MULTI_LINE_BOX ) )
912         {
913           // The last character of the text is a new paragraph character.
914           // An extra line with no characters is added to increase the text's height
915           // in order to place the cursor.
916
917           if( numberOfLines == linesCapacity )
918           {
919             // Reserve more space for the next lines.
920             linesBuffer = ResizeLinesBuffer( lines,
921                                              newLines,
922                                              linesCapacity,
923                                              updateCurrentBuffer );
924           }
925
926           UpdateTextLayout( layoutParameters,
927                             layout.characterIndex + layout.numberOfCharacters,
928                             index + layout.numberOfGlyphs,
929                             layoutSize,
930                             linesBuffer,
931                             numberOfLines );
932         } // whether to add a last line.
933
934         // Sets the positions of the glyphs.
935         SetGlyphPositions( glyphsBuffer + index,
936                            layout.numberOfGlyphs,
937                            outlineWidth,
938                            layoutParameters.interGlyphExtraAdvance,
939                            glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
940
941         // Updates the vertical pen's position.
942         penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
943
944         // Increase the glyph index.
945         index = nextIndex;
946       } // no ellipsis
947     } // end for() traversing glyphs.
948
949     if( updateCurrentBuffer )
950     {
951       glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
952                              newGlyphPositions.Begin(),
953                              newGlyphPositions.End() );
954       glyphPositions.Resize( totalNumberOfGlyphs );
955
956       newLines.Resize( numberOfLines );
957
958       // Current text's layout size adds only the newly laid-out lines.
959       // Updates the layout size with the previously laid-out lines.
960       UpdateLayoutSize( lines,
961                         layoutSize );
962
963       if( 0u != newLines.Count() )
964       {
965         const LineRun& lastLine = *( newLines.End() - 1u );
966
967         const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
968         const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
969
970         // Update the indices of the runs before the new laid-out lines are inserted.
971         UpdateLineIndexOffsets( layoutParameters,
972                                 lines,
973                                 characterOffset,
974                                 glyphOffset );
975
976         // Insert the lines.
977         lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
978                       newLines.Begin(),
979                       newLines.End() );
980       }
981     }
982     else
983     {
984       lines.Resize( numberOfLines );
985     }
986
987     // Rounds upward to avoid a non integer size.
988     layoutSize.height = std::ceil( layoutSize.height );
989
990     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
991
992     return true;
993   }
994
995   void ReLayoutRightToLeftLines( const Parameters& layoutParameters,
996                                  CharacterIndex startIndex,
997                                  Length numberOfCharacters,
998                                  Vector<Vector2>& glyphPositions )
999   {
1000     const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1001     const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1002     const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
1003     const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
1004
1005     const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
1006
1007     // Traverses the paragraphs with right to left characters.
1008     for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
1009     {
1010       const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
1011
1012       if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
1013       {
1014         // Do not reorder the line if it has been already reordered.
1015         continue;
1016       }
1017
1018       if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
1019       {
1020         // Do not reorder the lines after the last requested character.
1021         break;
1022       }
1023
1024       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
1025       const GlyphInfo& glyph = *( glyphsBuffer + *( charactersToGlyphsBuffer + characterVisualIndex ) );
1026
1027       float penX = -glyph.xBearing + outlineWidth + mCursorWidth;
1028
1029       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
1030
1031       // Traverses the characters of the right to left paragraph.
1032       for( CharacterIndex characterLogicalIndex = 0u;
1033            characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1034            ++characterLogicalIndex )
1035       {
1036         // Convert the character in the logical order into the character in the visual order.
1037         const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
1038
1039         // Get the number of glyphs of the character.
1040         const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterVisualIndex );
1041
1042         for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1043         {
1044           // Convert the character in the visual order into the glyph in the visual order.
1045           const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex ) + index;
1046
1047           DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count() );
1048
1049           const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
1050           Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1051
1052           position.x = std::round( penX + glyph.xBearing );
1053           penX += ( glyph.advance + layoutParameters.interGlyphExtraAdvance );
1054         }
1055       }
1056     }
1057   }
1058
1059   void Align( const Size& size,
1060               CharacterIndex startIndex,
1061               Length numberOfCharacters,
1062               Text::HorizontalAlignment::Type horizontalAlignment,
1063               Vector<LineRun>& lines,
1064               float& alignmentOffset,
1065               Dali::LayoutDirection::Type layoutDirection,
1066               bool matchSystemLanguageDirection )
1067   {
1068     const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1069
1070     alignmentOffset = MAX_FLOAT;
1071     // Traverse all lines and align the glyphs.
1072     for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1073          it != endIt;
1074          ++it )
1075     {
1076       LineRun& line = *it;
1077
1078       if( line.characterRun.characterIndex < startIndex )
1079       {
1080         // Do not align lines which have already been aligned.
1081         continue;
1082       }
1083
1084       if( line.characterRun.characterIndex > lastCharacterPlusOne )
1085       {
1086         // Do not align lines beyond the last laid-out character.
1087         break;
1088       }
1089
1090       if( line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast( lines, it ) )
1091       {
1092         // Do not align lines beyond the last laid-out character unless the line is last and empty.
1093         break;
1094       }
1095
1096       // Calculate the line's alignment offset accordingly with the align option,
1097       // the box width, line length, and the paragraph's direction.
1098       CalculateHorizontalAlignment( size.width,
1099                                     horizontalAlignment,
1100                                     line,
1101                                     layoutDirection,
1102                                     matchSystemLanguageDirection );
1103
1104       // Updates the alignment offset.
1105       alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1106     }
1107   }
1108
1109   void CalculateHorizontalAlignment( float boxWidth,
1110                                      HorizontalAlignment::Type horizontalAlignment,
1111                                      LineRun& line,
1112                                      Dali::LayoutDirection::Type layoutDirection,
1113                                      bool matchSystemLanguageDirection )
1114   {
1115     line.alignmentOffset = 0.f;
1116     const bool isLineRTL = RTL == line.direction;
1117     // Whether to swap the alignment.
1118     // Swap if the line is RTL and is not required to match the direction of the system's language or if it's required to match the direction of the system's language and it's RTL.
1119     bool isLayoutRTL = isLineRTL;
1120     float lineLength = line.width;
1121
1122     // match align for system language direction
1123     if( matchSystemLanguageDirection )
1124     {
1125       // Swap the alignment type if the line is right to left.
1126       isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1127     }
1128     // Calculate the horizontal line offset.
1129     switch( horizontalAlignment )
1130     {
1131       case HorizontalAlignment::BEGIN:
1132       {
1133         if( isLayoutRTL )
1134         {
1135           if( isLineRTL )
1136           {
1137             lineLength += line.extraLength;
1138           }
1139
1140           line.alignmentOffset = boxWidth - lineLength;
1141         }
1142         else
1143         {
1144           line.alignmentOffset = 0.f;
1145
1146           if( isLineRTL )
1147           {
1148             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1149             line.alignmentOffset -= line.extraLength;
1150           }
1151         }
1152         break;
1153       }
1154       case HorizontalAlignment::CENTER:
1155       {
1156         line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1157
1158         if( isLineRTL )
1159         {
1160           line.alignmentOffset -= line.extraLength;
1161         }
1162
1163         line.alignmentOffset = std::floor( line.alignmentOffset ); // floor() avoids pixel alignment issues.
1164         break;
1165       }
1166       case HorizontalAlignment::END:
1167       {
1168         if( isLayoutRTL )
1169         {
1170           line.alignmentOffset = 0.f;
1171
1172           if( isLineRTL )
1173           {
1174             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1175             line.alignmentOffset -= line.extraLength;
1176           }
1177         }
1178         else
1179         {
1180           if( isLineRTL )
1181           {
1182             lineLength += line.extraLength;
1183           }
1184
1185           line.alignmentOffset = boxWidth - lineLength;
1186         }
1187         break;
1188       }
1189     }
1190   }
1191
1192   void Initialize( LineRun& line )
1193   {
1194     line.glyphRun.glyphIndex = 0u;
1195     line.glyphRun.numberOfGlyphs = 0u;
1196     line.characterRun.characterIndex = 0u;
1197     line.characterRun.numberOfCharacters = 0u;
1198     line.width = 0.f;
1199     line.ascender = 0.f;
1200     line.descender = 0.f;
1201     line.extraLength = 0.f;
1202     line.alignmentOffset = 0.f;
1203     line.direction = LTR;
1204     line.ellipsis = false;
1205     line.lineSpacing = mDefaultLineSpacing;
1206   }
1207
1208   Type mLayout;
1209   float mCursorWidth;
1210   float mDefaultLineSpacing;
1211
1212   IntrusivePtr<Metrics> mMetrics;
1213 };
1214
1215 Engine::Engine()
1216 : mImpl( nullptr )
1217 {
1218   mImpl = new Engine::Impl();
1219 }
1220
1221 Engine::~Engine()
1222 {
1223   delete mImpl;
1224 }
1225
1226 void Engine::SetMetrics( MetricsPtr& metrics )
1227 {
1228   mImpl->mMetrics = metrics;
1229 }
1230
1231 void Engine::SetLayout( Type layout )
1232 {
1233   mImpl->mLayout = layout;
1234 }
1235
1236 Engine::Type Engine::GetLayout() const
1237 {
1238   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1239   return mImpl->mLayout;
1240 }
1241
1242 void Engine::SetCursorWidth( int width )
1243 {
1244   mImpl->mCursorWidth = static_cast<float>( width );
1245 }
1246
1247 int Engine::GetCursorWidth() const
1248 {
1249   return static_cast<int>( mImpl->mCursorWidth );
1250 }
1251
1252 bool Engine::LayoutText( const Parameters& layoutParameters,
1253                          Vector<Vector2>& glyphPositions,
1254                          Vector<LineRun>& lines,
1255                          Size& layoutSize,
1256                          bool elideTextEnabled,
1257                          bool& isAutoScrollEnabled )
1258 {
1259   return mImpl->LayoutText( layoutParameters,
1260                             glyphPositions,
1261                             lines,
1262                             layoutSize,
1263                             elideTextEnabled,
1264                             isAutoScrollEnabled );
1265 }
1266
1267 void Engine::ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1268                                        CharacterIndex startIndex,
1269                                        Length numberOfCharacters,
1270                                        Vector<Vector2>& glyphPositions )
1271 {
1272   mImpl->ReLayoutRightToLeftLines( layoutParameters,
1273                                    startIndex,
1274                                    numberOfCharacters,
1275                                    glyphPositions );
1276 }
1277
1278 void Engine::Align( const Size& size,
1279                     CharacterIndex startIndex,
1280                     Length numberOfCharacters,
1281                     Text::HorizontalAlignment::Type horizontalAlignment,
1282                     Vector<LineRun>& lines,
1283                     float& alignmentOffset,
1284                     Dali::LayoutDirection::Type layoutDirection,
1285                     bool matchSystemLanguageDirection )
1286 {
1287   mImpl->Align( size,
1288                 startIndex,
1289                 numberOfCharacters,
1290                 horizontalAlignment,
1291                 lines,
1292                 alignmentOffset,
1293                 layoutDirection,
1294                 matchSystemLanguageDirection );
1295 }
1296
1297 void Engine::SetDefaultLineSpacing( float lineSpacing )
1298 {
1299   mImpl->mDefaultLineSpacing = lineSpacing;
1300 }
1301
1302 float Engine::GetDefaultLineSpacing() const
1303 {
1304   return mImpl->mDefaultLineSpacing;
1305 }
1306
1307 } // namespace Layout
1308
1309 } // namespace Text
1310
1311 } // namespace Toolkit
1312
1313 } // namespace Dali