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