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