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