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