f7e9aa342aa0ad41adebbcd6ad478ed79f38f8c2
[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
49 } //namespace
50
51 /**
52  * @brief Stores temporary layout info of the line.
53  */
54 struct LineLayout
55 {
56   LineLayout()
57   : glyphIndex( 0u ),
58     characterIndex( 0u ),
59     numberOfGlyphs( 0u ),
60     numberOfCharacters( 0u ),
61     length( 0.f ),
62     extraBearing( 0.f ),
63     extraWidth( 0.f ),
64     wsLengthEndOfLine( 0.f ),
65     ascender( 0.f ),
66     descender( MAX_FLOAT )
67   {}
68
69   ~LineLayout()
70   {}
71
72   void Clear()
73   {
74     glyphIndex = 0u;
75     characterIndex = 0u;
76     numberOfGlyphs = 0u;
77     numberOfCharacters = 0u;
78     length = 0.f;
79     extraBearing = 0.f;
80     extraWidth = 0.f;
81     wsLengthEndOfLine = 0.f;
82     ascender = 0.f;
83     descender = MAX_FLOAT;
84   }
85
86   GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
87   CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
88   Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
89   Length         numberOfCharacters; ///< The number of characters which fit in one line.
90   float          length;             ///< The addition of the advance metric of all the glyphs which fit in one line.
91   float          extraBearing;       ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
92   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.
93   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
94   float          ascender;           ///< The maximum ascender of all fonts in the line.
95   float          descender;          ///< The minimum descender of all fonts in the line.
96 };
97
98 struct LayoutEngine::Impl
99 {
100   Impl()
101   : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
102     mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
103     mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP ),
104     mEllipsisEnabled( false )
105   {
106     mFontClient = TextAbstraction::FontClient::Get();
107   }
108
109   /**
110    * @brief Updates the line ascender and descender with the metrics of a new font.
111    *
112    * @param[in] fontId The id of the new font.
113    * @param[in,out] lineLayout The line layout.
114    */
115   void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
116   {
117     Text::FontMetrics fontMetrics;
118     mFontClient.GetFontMetrics( fontId, fontMetrics );
119
120     // Sets the maximum ascender.
121     if( fontMetrics.ascender > lineLayout.ascender )
122     {
123       lineLayout.ascender = fontMetrics.ascender;
124     }
125
126     // Sets the minimum descender.
127     if( fontMetrics.descender < lineLayout.descender )
128     {
129       lineLayout.descender = fontMetrics.descender;
130     }
131   }
132
133   /**
134    * @brief Merges a temporary line layout into the line layout.
135    *
136    * @param[in,out] lineLayout The line layout.
137    * @param[in] tmpLineLayout A temporary line layout.
138    */
139   void MergeLineLayout( LineLayout& lineLayout,
140                         const LineLayout& tmpLineLayout )
141   {
142     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
143     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
144     lineLayout.length += tmpLineLayout.length;
145
146     if( 0.f < tmpLineLayout.length )
147     {
148       lineLayout.length += lineLayout.wsLengthEndOfLine;
149
150       lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
151     }
152     else
153     {
154       lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
155     }
156
157     if( tmpLineLayout.ascender > lineLayout.ascender )
158     {
159       lineLayout.ascender = tmpLineLayout.ascender;
160     }
161
162     if( tmpLineLayout.descender < lineLayout.descender )
163     {
164       lineLayout.descender = tmpLineLayout.descender;
165     }
166   }
167
168   /**
169    * Retrieves the line layout for a given box width.
170    *
171    * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
172    *       because the number of characters of the line is not known (one of the responsabilities of this method
173    *       is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
174    *       and left to right text is laid out, it can be small differences in the line length. One solution is to
175    *       reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
176    *       this method calculates which are the first and last glyphs of the line (the ones that causes the
177    *       differences). This is a good point to check if there is problems with the text exceeding the boundaries
178    *       of the control when there is right to left text.
179    *
180    * @param[in] parameters The layout parameters.
181    * @param[out] lineLayout The line layout.
182    * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
183    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
184    */
185   void GetLineLayoutForBox( const LayoutParameters& parameters,
186                             LineLayout& lineLayout,
187                             CharacterDirection& paragraphDirection,
188                             bool completelyFill )
189   {
190     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
191     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex );
192     // Stores temporary line layout which has not been added to the final line layout.
193     LineLayout tmpLineLayout;
194
195     const bool isMultiline = mLayout == MULTI_LINE_BOX;
196     const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
197
198     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
199     // In the case the line starts with a right to left character, if the width is longer than the advance,
200     // the difference needs to be added to the line length.
201     const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + lineLayout.glyphIndex );
202
203     // Set the direction of the first character of the line.
204     lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
205     const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
206     CharacterDirection previousCharacterDirection = firstCharacterDirection;
207
208     const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
209     float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
210
211     float tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
212
213     tmpLineLayout.length += 1.f; // Added one unit to give some space to the cursor.
214
215     // Calculate the line height if there is no characters.
216     FontId lastFontId = glyphInfo.fontId;
217     UpdateLineHeight( lastFontId, tmpLineLayout );
218
219     bool oneWordLaidOut = false;
220
221     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
222          glyphIndex < parameters.totalNumberOfGlyphs;
223          ++glyphIndex )
224     {
225       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  glyph index : %d\n", glyphIndex );
226       const bool isLastGlyph = glyphIndex == lastGlyphIndex;
227
228       // Get the glyph info.
229       const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
230
231       // Check if the font of the current glyph is the same of the previous one.
232       // If it's different the ascender and descender need to be updated.
233       if( lastFontId != glyphInfo.fontId )
234       {
235         UpdateLineHeight( glyphInfo.fontId, tmpLineLayout );
236         lastFontId = glyphInfo.fontId;
237       }
238
239       // Get the character indices for the current glyph. The last character index is needed
240       // because there are glyphs formed by more than one character but their break info is
241       // given only for the last character.
242       const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
243       const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
244       const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
245
246       // Get the line break info for the current character.
247       const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
248
249       // Get the word break info for the current character.
250       const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
251
252       // Increase the number of characters.
253       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
254
255       // Increase the number of glyphs.
256       tmpLineLayout.numberOfGlyphs++;
257
258       // Check whether is a white space.
259       const Character character = *( parameters.textBuffer + characterFirstIndex );
260       const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
261
262       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
263       const float previousTmpLineLength = tmpLineLayout.length;
264       const float previousTmpExtraBearing = tmpExtraBearing;
265       const float previousTmpExtraWidth = tmpExtraWidth;
266
267       // Get the character's direction.
268       const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
269
270       // Increase the accumulated length.
271       if( isWhiteSpace )
272       {
273         // Add the length to the length of white spaces at the end of the line.
274         tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // The advance is used as the width is always zero for the white spaces.
275       }
276       else
277       {
278         // Add as well any previous white space length.
279         tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
280
281         // An extra space may be added to the line for the first and last glyph of the line.
282         // If the bearing of the first glyph is negative, its positive value needs to be added.
283         // If the bearing plus the width of the last glyph is greater than the advance, the difference
284         // needs to be added.
285
286         if( characterDirection == paragraphDirection )
287         {
288           if( RTL == characterDirection )
289           {
290             //       <--
291             // |   Rrrrr|
292             // or
293             // |  Rllrrr|
294             // or
295             // |lllrrrrr|
296             // |     Rll|
297             //
298
299             tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
300           }
301           else // LTR
302           {
303             //  -->
304             // |lllL    |
305             // or
306             // |llrrL   |
307             // or
308             // |lllllrrr|
309             // |rrL     |
310             //
311
312             const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
313             tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
314           }
315         }
316         else
317         {
318           if( characterDirection != previousCharacterDirection )
319           {
320             if( RTL == characterDirection )
321             {
322               //  -->
323               // |lllR    |
324
325               const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
326               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
327             }
328             else // LTR
329             {
330               //       <--
331               // |   Lrrrr|
332
333               tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
334             }
335           }
336           else if( characterDirection == firstCharacterDirection )
337           {
338             if( RTL == characterDirection )
339             {
340               //  -->
341               // |llllllrr|
342               // |Rr      |
343
344               tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
345             }
346             else // LTR
347             {
348               //       <--
349               // |llllrrrr|
350               // |     llL|
351
352               const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
353               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
354             }
355           }
356         }
357
358         // Clear the white space length at the end of the line.
359         tmpLineLayout.wsLengthEndOfLine = 0.f;
360       }
361
362       // Check if the accumulated length fits in the width of the box.
363       if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
364           ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
365       {
366         // Current word does not fit in the box's width.
367         if( !oneWordLaidOut || completelyFill )
368         {
369           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Break the word by character\n" );
370
371           // The word's with doesn't fit in the control's with. It needs to be split by character.
372           if( tmpLineLayout.numberOfGlyphs > 0u )
373           {
374             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
375             --tmpLineLayout.numberOfGlyphs;
376             tmpLineLayout.length = previousTmpLineLength;
377             tmpExtraBearing = previousTmpExtraBearing;
378             tmpExtraWidth = previousTmpExtraWidth;
379           }
380
381           // Add part of the word to the line layout.
382           MergeLineLayout( lineLayout, tmpLineLayout );
383         }
384         else
385         {
386           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Current word does not fit.\n" );
387         }
388
389         lineLayout.extraBearing = tmpExtraBearing;
390         lineLayout.extraWidth = tmpExtraWidth;
391
392         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
393
394         return;
395       }
396
397       if( ( isMultiline || isLastGlyph ) &&
398           ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
399       {
400         // Must break the line. Update the line layout and return.
401         MergeLineLayout( lineLayout, tmpLineLayout );
402
403         // Set the next paragraph's direction.
404         if( !isLastGlyph &&
405             ( NULL != parameters.characterDirectionBuffer ) )
406         {
407           paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
408         }
409
410         lineLayout.extraBearing = tmpExtraBearing;
411         lineLayout.extraWidth = tmpExtraWidth;
412
413         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Must break\n" );
414         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
415         return;
416       }
417
418       if( isMultiline &&
419           ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
420       {
421         oneWordLaidOut = true;
422         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  One word laid out\n" );
423
424         // Current glyph is the last one of the current word.
425         // Add the temporal layout to the current one.
426         MergeLineLayout( lineLayout, tmpLineLayout );
427
428         tmpLineLayout.Clear();
429       }
430
431       previousCharacterDirection = characterDirection;
432     }
433
434     lineLayout.extraBearing = tmpExtraBearing;
435     lineLayout.extraWidth = tmpExtraWidth;
436
437     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
438   }
439
440   void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
441                           Length numberOfGlyphs,
442                           float penY,
443                           Vector2* glyphPositionsBuffer )
444   {
445     // Traverse the glyphs and set the positions.
446
447     // Check if the x bearing of the first character is negative.
448     // If it has a negative x bearing, it will exceed the boundaries of the actor,
449     // so the penX position needs to be moved to the right.
450
451     const GlyphInfo& glyph = *glyphsBuffer;
452     float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
453     penX += 1.f; // Added one unit to give some space to the cursor.
454
455     for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
456     {
457       const GlyphInfo& glyph = *( glyphsBuffer + i );
458       Vector2& position = *( glyphPositionsBuffer + i );
459
460       position.x = penX + glyph.xBearing;
461       position.y = penY - glyph.yBearing;
462
463       penX += glyph.advance;
464     }
465   }
466
467   bool LayoutText( const LayoutParameters& layoutParameters,
468                    Vector<Vector2>& glyphPositions,
469                    Vector<LineRun>& lines,
470                    Size& actualSize )
471   {
472     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
473     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
474
475     // Set the first paragraph's direction.
476     CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
477
478     float penY = 0.f;
479     for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
480     {
481       CharacterDirection currentParagraphDirection = paragraphDirection;
482
483       // Get the layout for the line.
484       LineLayout layout;
485       layout.glyphIndex = index;
486       GetLineLayoutForBox( layoutParameters,
487                            layout,
488                            paragraphDirection,
489                            false );
490
491       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex );
492       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex );
493       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs );
494       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters );
495       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                length %f\n", layout.length );
496
497       if( 0u == layout.numberOfGlyphs )
498       {
499         // The width is too small and no characters are laid-out.
500         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
501         return false;
502       }
503
504       // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
505       // of the box.
506       penY += layout.ascender;
507
508       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  pen y %f\n", penY );
509       if( mEllipsisEnabled &&
510           ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
511             ( ( mLayout == SINGLE_LINE_BOX ) &&
512               ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) ) )
513       {
514         // Do not layout more lines if ellipsis is enabled.
515
516         // The last line needs to be completely filled with characters.
517         // Part of a word may be used.
518
519         const Length numberOfLines = lines.Count();
520
521         LineRun lineRun;
522         LineLayout ellipsisLayout;
523         if( 0u != numberOfLines )
524         {
525           // Get the last line and layout it again with the 'completelyFill' flag to true.
526           lineRun = *( lines.Begin() + ( numberOfLines - 1u ) );
527
528           penY -= layout.ascender - lineRun.descender;
529
530           ellipsisLayout.glyphIndex = lineRun.glyphIndex;
531         }
532         else
533         {
534           lineRun.glyphIndex = 0u;
535           ellipsisLayout.glyphIndex = 0u;
536         }
537
538         GetLineLayoutForBox( layoutParameters,
539                              ellipsisLayout,
540                              currentParagraphDirection,
541                              true );
542
543         lineRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
544         lineRun.characterRun.characterIndex = ellipsisLayout.characterIndex;
545         lineRun.characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
546         lineRun.width = ellipsisLayout.length;
547         lineRun.extraLength =  ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
548         lineRun.ascender = ellipsisLayout.ascender;
549         lineRun.descender = ellipsisLayout.descender;
550         lineRun.ellipsis = true;
551
552         actualSize.width = layoutParameters.boundingBox.width;
553         actualSize.height += ( lineRun.ascender + -lineRun.descender );
554
555         SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun.glyphIndex,
556                            ellipsisLayout.numberOfGlyphs,
557                            penY,
558                            glyphPositions.Begin() + lineRun.glyphIndex );
559
560         if( 0u != numberOfLines )
561         {
562           // Set the last line with the ellipsis layout.
563           *( lines.Begin() + ( numberOfLines - 1u ) ) = lineRun;
564         }
565         else
566         {
567           // Push the line.
568           lines.PushBack( lineRun );
569         }
570
571         break;
572       }
573       else
574       {
575         const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
576
577         LineRun lineRun;
578         lineRun.glyphIndex = index;
579         lineRun.numberOfGlyphs = layout.numberOfGlyphs;
580         lineRun.characterRun.characterIndex = layout.characterIndex;
581         lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
582         if( isLastLine && !layoutParameters.isLastNewParagraph )
583         {
584           const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
585           if( MULTI_LINE_BOX == mLayout )
586           {
587             lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
588           }
589           else
590           {
591             lineRun.width = width;
592           }
593
594           lineRun.extraLength = 0.f;
595         }
596         else
597         {
598           lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
599           lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
600         }
601         lineRun.ascender = layout.ascender;
602         lineRun.descender = layout.descender;
603         lineRun.direction = false;
604         lineRun.ellipsis = false;
605
606         lines.PushBack( lineRun );
607
608         // Update the actual size.
609         if( lineRun.width > actualSize.width )
610         {
611           actualSize.width = lineRun.width;
612         }
613
614         actualSize.height += ( lineRun.ascender + -lineRun.descender );
615
616         SetGlyphPositions( layoutParameters.glyphsBuffer + index,
617                            layout.numberOfGlyphs,
618                            penY,
619                            glyphPositions.Begin() + index );
620
621         penY += -layout.descender;
622
623         // Increase the glyph index.
624         index += layout.numberOfGlyphs;
625
626         if( isLastLine &&
627             layoutParameters.isLastNewParagraph &&
628             ( mLayout == MULTI_LINE_BOX ) )
629         {
630           // Need to add a new line with no characters but with height to increase the actualSize.height
631           const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
632
633           Text::FontMetrics fontMetrics;
634           mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
635
636           LineRun lineRun;
637           lineRun.glyphIndex = 0u;
638           lineRun.numberOfGlyphs = 0u;
639           lineRun.characterRun.characterIndex = 0u;
640           lineRun.characterRun.numberOfCharacters = 0u;
641           lineRun.width = 0.f;
642           lineRun.ascender = fontMetrics.ascender;
643           lineRun.descender = fontMetrics.descender;
644           lineRun.extraLength = 0.f;
645           lineRun.alignmentOffset = 0.f;
646           lineRun.direction = !RTL;
647           lineRun.ellipsis = false;
648
649           actualSize.height += ( lineRun.ascender + -lineRun.descender );
650
651           lines.PushBack( lineRun );
652         }
653       }
654     } // end for() traversing glyphs.
655
656     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
657
658     return true;
659   }
660
661   void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
662                                  Vector<Vector2>& glyphPositions )
663   {
664     // Traverses the paragraphs with right to left characters.
665     for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
666     {
667       const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
668
669       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
670       const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
671
672       float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
673       penX += 1.f; // Added one unit to give some space to the cursor.
674
675       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
676
677       // Traverses the characters of the right to left paragraph.
678       for( CharacterIndex characterLogicalIndex = 0u;
679            characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
680            ++characterLogicalIndex )
681       {
682         // Convert the character in the logical order into the character in the visual order.
683         const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
684
685         // Get the number of glyphs of the character.
686         const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
687
688         for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
689         {
690           // Convert the character in the visual order into the glyph in the visual order.
691           const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
692
693           DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
694
695           const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
696           Vector2& position = *( glyphPositionsBuffer + glyphIndex );
697
698           position.x = penX + glyph.xBearing;
699           penX += glyph.advance;
700         }
701       }
702     }
703   }
704
705   void Align( const Size& layoutSize,
706               Vector<LineRun>& lines )
707   {
708     // Traverse all lines and align the glyphs.
709
710     for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
711          it != endIt;
712          ++it )
713     {
714       LineRun& line = *it;
715       const bool isLastLine = lines.End() == it + 1u;
716
717       // Calculate the alignment offset accordingly with the align option,
718       // the box width, line length, and the paragraphs direction.
719       CalculateHorizontalAlignment( layoutSize.width,
720                                     line,
721                                     isLastLine );
722     }
723   }
724
725   void CalculateHorizontalAlignment( float boxWidth,
726                                      LineRun& line,
727                                      bool isLastLine )
728   {
729     line.alignmentOffset = 0.f;
730     const bool isRTL = RTL == line.direction;
731     float lineLength = line.width;
732
733     HorizontalAlignment alignment = mHorizontalAlignment;
734     if( isRTL &&
735         ( HORIZONTAL_ALIGN_CENTER != alignment ) )
736     {
737       if( HORIZONTAL_ALIGN_BEGIN == alignment )
738       {
739         alignment = HORIZONTAL_ALIGN_END;
740       }
741       else
742       {
743         alignment = HORIZONTAL_ALIGN_BEGIN;
744       }
745     }
746
747     switch( alignment )
748     {
749       case HORIZONTAL_ALIGN_BEGIN:
750       {
751         line.alignmentOffset = 0.f;
752
753         if( isRTL )
754         {
755           // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
756           line.alignmentOffset -= line.extraLength;
757
758           if( isLastLine )
759           {
760             line.alignmentOffset += std::min( line.extraLength, boxWidth - lineLength );
761           }
762         }
763         break;
764       }
765       case HORIZONTAL_ALIGN_CENTER:
766       {
767         if( isLastLine && !isRTL )
768         {
769           lineLength += line.extraLength;
770           if( lineLength > boxWidth )
771           {
772             lineLength = boxWidth;
773             line.alignmentOffset = 0.f;
774             break;
775           }
776         }
777
778         line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
779
780         if( isRTL )
781         {
782           line.alignmentOffset -= line.extraLength;
783
784           if( isLastLine )
785           {
786             line.alignmentOffset += 0.5f * std::min( line.extraLength, boxWidth - lineLength );
787           }
788         }
789
790         line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
791         break;
792       }
793       case HORIZONTAL_ALIGN_END:
794       {
795         if( isLastLine && !isRTL )
796         {
797           lineLength += line.extraLength;
798           if( lineLength > boxWidth )
799           {
800             line.alignmentOffset = 0.f;
801             break;
802           }
803         }
804
805         if( isRTL )
806         {
807           lineLength += line.extraLength;
808         }
809
810         line.alignmentOffset = boxWidth - lineLength;
811         break;
812       }
813     }
814   }
815
816   LayoutEngine::Layout mLayout;
817   LayoutEngine::HorizontalAlignment mHorizontalAlignment;
818   LayoutEngine::VerticalAlignment mVerticalAlignment;
819
820   TextAbstraction::FontClient mFontClient;
821
822   bool mEllipsisEnabled:1;
823 };
824
825 LayoutEngine::LayoutEngine()
826 : mImpl( NULL )
827 {
828   mImpl = new LayoutEngine::Impl();
829 }
830
831 LayoutEngine::~LayoutEngine()
832 {
833   delete mImpl;
834 }
835
836 void LayoutEngine::SetLayout( Layout layout )
837 {
838   mImpl->mLayout = layout;
839 }
840
841 unsigned int LayoutEngine::GetLayout() const
842 {
843   return mImpl->mLayout;
844 }
845
846 void LayoutEngine::SetTextEllipsisEnabled( bool enabled )
847 {
848   mImpl->mEllipsisEnabled = enabled;
849 }
850
851 bool LayoutEngine::GetTextEllipsisEnabled() const
852 {
853   return mImpl->mEllipsisEnabled;
854 }
855
856 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
857 {
858   mImpl->mHorizontalAlignment = alignment;
859 }
860
861 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
862 {
863   return mImpl->mHorizontalAlignment;
864 }
865
866 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
867 {
868   mImpl->mVerticalAlignment = alignment;
869 }
870
871 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
872 {
873   return mImpl->mVerticalAlignment;
874 }
875
876 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
877                                Vector<Vector2>& glyphPositions,
878                                Vector<LineRun>& lines,
879                                Size& actualSize )
880 {
881   return mImpl->LayoutText( layoutParameters,
882                             glyphPositions,
883                             lines,
884                             actualSize );
885 }
886
887 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
888                                              Vector<Vector2>& glyphPositions )
889 {
890   mImpl->ReLayoutRightToLeftLines( layoutParameters,
891                                    glyphPositions );
892 }
893
894 void LayoutEngine::Align( const Size& layoutSize,
895                           Vector<LineRun>& lines )
896 {
897   mImpl->Align( layoutSize,
898                 lines );
899 }
900
901 } // namespace Text
902
903 } // namespace Toolkit
904
905 } // namespace Dali