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