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