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