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