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