[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-view.cpp
1 /*
2  * Copyright (c) 2023 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/text-view.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/math/vector2.h>
24 #include <memory.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
28 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
29
30 namespace Dali::Toolkit::Text
31 {
32
33 namespace
34 {
35
36 /// If ellipsis is enabled, calculate the number of laid out glyphs.
37 /// Otherwise use the given number of glyphs.
38 void CalculateNumberOfLaidOutGlyphes(
39     const bool                               hasEllipsis,
40     bool&                                    textElided,
41     Length&                                  numberOfLaidOutGlyphs,
42     Length&                                  numberOfActualLaidOutGlyphs,
43     const Length&                            numberOfGlyphs,
44     const DevelText::EllipsisPosition::Type& ellipsisPosition,
45     const LineRun*&                          ellipsisLine,
46     const Length&                            numberOfLines,
47     const LineRun* const&                    lines)
48 {
49   if(hasEllipsis)
50   {
51     textElided            = true;
52     numberOfLaidOutGlyphs = numberOfGlyphs;
53
54     switch(ellipsisPosition)
55     {
56       case DevelText::EllipsisPosition::START:
57       {
58         numberOfActualLaidOutGlyphs = numberOfGlyphs - ellipsisLine->glyphRun.glyphIndex;
59         break;
60       }
61       case DevelText::EllipsisPosition::MIDDLE:
62       {
63         numberOfActualLaidOutGlyphs = 0u;
64         for(Length lineIndex = 0u ; lineIndex < numberOfLines ; lineIndex++)
65         {
66           numberOfActualLaidOutGlyphs += lines[lineIndex].glyphRun.numberOfGlyphs + lines[lineIndex].glyphRunSecondHalf.numberOfGlyphs;
67         }
68         break;
69       }
70       case DevelText::EllipsisPosition::END:
71       {
72         numberOfActualLaidOutGlyphs = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs;
73         break;
74       }
75     }
76   }
77   else
78   {
79     numberOfActualLaidOutGlyphs = numberOfLaidOutGlyphs = numberOfGlyphs;
80   }
81 }
82
83 void InsertEllipsisGlyph(
84   GlyphInfo*& glyphs,
85   GlyphIndex& indexOfEllipsis,
86   Length& numberOfRemovedGlyphs,
87   Vector2*& glyphPositions,
88   TextAbstraction::FontClient& fontClient,
89   const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns,
90   const float& modelCharacterSpacing,
91   float& calculatedAdvance,
92   const Character*& textBuffer,
93   const CharacterIndex*& glyphToCharacterMapBuffer,
94   const Length& numberOfGlyphs,
95   const bool isTailMode,
96   const LineRun*& ellipsisLine,
97   const Length& numberOfLaidOutGlyphs)
98 {
99   // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
100   float firstPenX   = 0.f; // Used if rtl text is elided.
101   float penY        = 0.f;
102   bool  firstPenSet = false;
103   bool  inserted    = false;
104
105   float removedGlyphsWidth = 0.f;
106
107   while(!inserted)
108   {
109     const GlyphInfo& glyphToRemove = *(glyphs + indexOfEllipsis);
110
111     if(0u != glyphToRemove.fontId)
112     {
113       // i.e. The font id of the glyph shaped from the '\n' character is zero.
114
115       // Need to reshape the glyph as the font may be different in size.
116       const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId));
117
118       if(!firstPenSet)
119       {
120         const Vector2& position = *(glyphPositions + indexOfEllipsis);
121
122         // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
123         penY = position.y + glyphToRemove.yBearing;
124
125         // Calculates the first penX which will be used if rtl text is elided.
126         firstPenX = position.x - glyphToRemove.xBearing;
127         if(firstPenX < -ellipsisGlyph.xBearing)
128         {
129           // Avoids to exceed the bounding box when rtl text is elided.
130           firstPenX = -ellipsisGlyph.xBearing;
131         }
132
133         removedGlyphsWidth = -ellipsisGlyph.xBearing;
134
135         firstPenSet = true;
136       }
137
138       const float characterSpacing = GetGlyphCharacterSpacing(indexOfEllipsis, characterSpacingGlyphRuns, modelCharacterSpacing);
139       calculatedAdvance            = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + indexOfEllipsis))), characterSpacing, glyphToRemove.advance);
140       removedGlyphsWidth += std::min(calculatedAdvance, (glyphToRemove.xBearing + glyphToRemove.width));
141
142       // Calculate the width of the ellipsis glyph and check if it fits.
143       const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
144       if((ellipsisGlyphWidth < removedGlyphsWidth) || (isTailMode ? (indexOfEllipsis == 0u) : (indexOfEllipsis == numberOfGlyphs - 1u)))
145       {
146         GlyphInfo& glyphInfo = *(glyphs + indexOfEllipsis);
147         Vector2&   position  = *(glyphPositions + indexOfEllipsis);
148         position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f;
149
150         // Replace the glyph by the ellipsis glyph.
151         glyphInfo = ellipsisGlyph;
152
153         // Change the 'x' and 'y' position of the ellipsis glyph.
154         if(position.x > firstPenX)
155         {
156           if(isTailMode)
157           {
158             // To handle case of the mixed languages (LTR then RTL) with
159             // EllipsisPosition::END and the LayoutDirection::RIGHT_TO_LEFT
160             float nextXPositions = ellipsisLine->width;
161             if(indexOfEllipsis + 1u < numberOfGlyphs)
162             {
163               Vector2& positionOfNextGlyph = *(glyphPositions + indexOfEllipsis + 1u);
164               nextXPositions               = positionOfNextGlyph.x;
165             }
166
167             if(position.x > nextXPositions) // RTL language
168             {
169               if((indexOfEllipsis > 0u) && ((position.x - nextXPositions) > removedGlyphsWidth))
170               {
171                 // To handle mixed directions
172                 // Re-calculates the first penX which will be used if rtl text is elided.
173                 firstPenX = position.x - glyphToRemove.xBearing;
174                 if(firstPenX < -ellipsisGlyph.xBearing)
175                 {
176                   // Avoids to exceed the bounding box when rtl text is elided.
177                   firstPenX = -ellipsisGlyph.xBearing;
178                 }
179                 //Reset the width of removed glyphs
180                 removedGlyphsWidth = std::min(calculatedAdvance, (glyphToRemove.xBearing + glyphToRemove.width)) - ellipsisGlyph.xBearing;
181
182                 --indexOfEllipsis;
183                 continue;
184               }
185               else
186               {
187                 // To handle the case of RTL language with EllipsisPosition::END
188                 position.x = firstPenX + removedGlyphsWidth - ellipsisGlyphWidth;
189               }
190             }
191           }
192           else
193           {
194             // To handle the case of LTR language with EllipsisPosition::START
195             position.x = firstPenX + removedGlyphsWidth - ellipsisGlyphWidth;
196           }
197         }
198         else
199         {
200           if(!isTailMode)
201           {
202             // To handle case of the mixed languages (RTL then LTR) with
203             // EllipsisPosition::START and the LayoutDirection::RIGHT_TO_LEFT
204             float nextXPositions = ellipsisLine->width;
205             if(indexOfEllipsis + 1u < numberOfGlyphs)
206             {
207               Vector2& positionOfNextGlyph = *(glyphPositions + indexOfEllipsis + 1u);
208               nextXPositions               = positionOfNextGlyph.x;
209             }
210
211             if(position.x < nextXPositions) // LTR language
212             {
213               position.x = firstPenX + removedGlyphsWidth - ellipsisGlyphWidth;
214
215               if((position.x + ellipsisGlyphWidth + ellipsisGlyph.xBearing) > nextXPositions)
216               {
217                 position.x -= (position.x + ellipsisGlyphWidth + ellipsisGlyph.xBearing) - nextXPositions;
218               }
219             }
220           }
221         }
222
223         position.x += ellipsisGlyph.xBearing;
224         position.y = penY - ellipsisGlyph.yBearing;
225
226         inserted = true;
227       }
228     }
229
230     if(!inserted)
231     {
232       if(isTailMode && indexOfEllipsis > 0u)
233       {
234         // Tail Mode: remove glyphs from startIndexOfEllipsis then decrement indexOfEllipsis, until arrive to index zero.
235         --indexOfEllipsis;
236       }
237       else if(!isTailMode && indexOfEllipsis < numberOfLaidOutGlyphs - 1u)
238       {
239         // Not Tail Mode: remove glyphs from startIndexOfEllipsis then increase indexOfEllipsis, until arrive to last index (numberOfGlyphs - 1u).
240         ++indexOfEllipsis;
241       }
242       else
243       {
244         // No space for the ellipsis.
245         inserted = true;
246       }
247       ++numberOfRemovedGlyphs;
248     }
249   }
250 }
251
252 /// 'Removes' all the glyphs after the ellipsis glyph.
253 void RemoveAllGlyphsAfterEllipsisGlyph(
254     const DevelText::EllipsisPosition::Type& ellipsisPosition,
255     Length& numberOfLaidOutGlyphs,
256     const Length& numberOfActualLaidOutGlyphs,
257     const Length& numberOfRemovedGlyphs,
258     const bool isTailMode,
259     const GlyphIndex& indexOfEllipsis,
260     const LineRun*& ellipsisNextLine,
261     const LineRun*& ellipsisLine,
262     GlyphInfo*& glyphs,
263     Vector2*& glyphPositions,
264     const Length& numberOfGlyphs,
265     const GlyphIndex& startIndexOfEllipsis,
266     VisualModelPtr& visualModel)
267 {
268   switch(ellipsisPosition)
269   {
270     case DevelText::EllipsisPosition::MIDDLE:
271     {
272       //Reduce size, shift glyphs and start from ellipsis glyph
273       numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
274
275       GlyphIndex firstMiddleIndexOfElidedGlyphs  = 0u;
276       GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
277
278       bool isOnlySecondHalf = false;
279       if(isTailMode)
280       {
281         // Multi-lines case with MIDDLE
282         // In case the Ellipsis in the end of line,
283         // then this index will be the firstMiddleIndex.
284         // The secondMiddleIndex will be the fisrt index in next line.
285         // But in case there is no line after Ellipsis's line then secondMiddleIndex and endIndex equal firstMiddle
286         // Example:
287         // A: are laid out glyphs in line has Ellipsis in the end.
288         // N: are laid out glyphs in lines after removed lines.
289         // R: are removed glyphs.
290         // L: are removed glyphs when removed lines.
291         // AAAAAAAAAAAA...RRR    => Here's the firstMiddleIndex (First index after last A)
292         // LLLLLLLLLLLLLLL
293         // LLLLLLLLLLLLLLL
294         // NNNNNNNNNNNNNN        => Here's the secondMiddleIndex (First N)
295         // NNNNNNNNNN
296
297         firstMiddleIndexOfElidedGlyphs = indexOfEllipsis;
298         if(ellipsisNextLine != nullptr)
299         {
300           secondMiddleIndexOfElidedGlyphs = ellipsisNextLine->glyphRun.glyphIndex;
301         } else
302         {
303           secondMiddleIndexOfElidedGlyphs = firstMiddleIndexOfElidedGlyphs;
304           visualModel->SetEndIndexOfElidedGlyphs(firstMiddleIndexOfElidedGlyphs);
305         }
306       } else
307       {
308         // Single line case with MIDDLE
309         // In case the Ellipsis in the middle of line,
310         // Then the last index in first half will be firstMiddleIndex.
311         // And the indexOfEllipsis will be secondMiddleIndex, which is the first index in second half.
312         // Example:
313         // A: are laid out glyphs in first half of line.
314         // N: are laid out glyphs in second half of line.
315         // R: are removed glyphs.
316         // L: re removed glyphs when layouting text
317         // AAAAAAALLLLLLLLLLLRRR...NNNNN
318         // firstMiddleIndex (index of last A)
319         // secondMiddleIndex (index before first N)
320
321         firstMiddleIndexOfElidedGlyphs  = (ellipsisLine->glyphRun.numberOfGlyphs > 0u) ? (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u) : (ellipsisLine->glyphRun.glyphIndex);
322         secondMiddleIndexOfElidedGlyphs = indexOfEllipsis;
323         isOnlySecondHalf                = ellipsisLine->glyphRun.numberOfGlyphs == 0u && ellipsisLine->glyphRunSecondHalf.numberOfGlyphs > 0u;
324       }
325
326       visualModel->SetFirstMiddleIndexOfElidedGlyphs(firstMiddleIndexOfElidedGlyphs);
327       visualModel->SetSecondMiddleIndexOfElidedGlyphs(secondMiddleIndexOfElidedGlyphs);
328
329       // The number of shifted glyphs and shifting positions will be different according to Single-line or Multi-lines.
330       // isOnlySecondHalf will be true when MIDDLE Ellipsis glyph in single line.
331       if(isOnlySecondHalf)
332       {
333         Length numberOfSecondHalfGlyphs = numberOfLaidOutGlyphs - firstMiddleIndexOfElidedGlyphs;
334
335         //Copy elided glyphs after the ellipsis glyph.
336         memcpy(glyphs + firstMiddleIndexOfElidedGlyphs, glyphs + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo));
337         memcpy(glyphPositions + firstMiddleIndexOfElidedGlyphs, glyphPositions + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2));
338       } else
339       {
340         Length numberOfSecondHalfGlyphs = numberOfLaidOutGlyphs - firstMiddleIndexOfElidedGlyphs + 1u;
341
342         // Make sure that out-of-boundary does not occur.
343         if(secondMiddleIndexOfElidedGlyphs + numberOfSecondHalfGlyphs > numberOfGlyphs)
344         {
345           numberOfSecondHalfGlyphs = numberOfGlyphs - secondMiddleIndexOfElidedGlyphs;
346         }
347
348         //Copy elided glyphs after the ellipsis glyph.
349         memcpy(glyphs + firstMiddleIndexOfElidedGlyphs + 1u, glyphs + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo));
350         memcpy(glyphPositions + firstMiddleIndexOfElidedGlyphs + 1u, glyphPositions + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2));
351       }
352       break;
353     }
354
355     case DevelText::EllipsisPosition::START:
356     {
357       numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
358       //Copy elided glyphs after the ellipsis glyph.
359       memcpy(glyphs, glyphs + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(GlyphInfo));
360       memcpy(glyphPositions, glyphPositions + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(Vector2));
361       visualModel->SetStartIndexOfElidedGlyphs(indexOfEllipsis);
362       break;
363     }
364
365     case DevelText::EllipsisPosition::END:
366     {
367       numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
368       visualModel->SetEndIndexOfElidedGlyphs(indexOfEllipsis);
369       break;
370     }
371   }
372 } // unnamed namespace
373
374 }
375 struct View::Impl
376 {
377   VisualModelPtr              mVisualModel;
378   LogicalModelPtr             mLogicalModel;
379   TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
380 };
381
382 View::View()
383     : mImpl(NULL)
384 {
385   mImpl = new View::Impl();
386
387   mImpl->mFontClient = TextAbstraction::FontClient::Get();
388 }
389
390 View::~View()
391 {
392   delete mImpl;
393 }
394
395 void View::SetVisualModel(VisualModelPtr visualModel)
396 {
397   mImpl->mVisualModel = visualModel;
398 }
399
400 void View::SetLogicalModel(LogicalModelPtr logicalModel)
401 {
402   mImpl->mLogicalModel = logicalModel;
403 }
404
405 const Vector2& View::GetControlSize() const
406 {
407   if(mImpl->mVisualModel)
408   {
409     return mImpl->mVisualModel->mControlSize;
410   }
411
412   return Vector2::ZERO;
413 }
414
415 const Vector2& View::GetLayoutSize() const
416 {
417   if(mImpl->mVisualModel)
418   {
419     return mImpl->mVisualModel->GetLayoutSize();
420   }
421
422   return Vector2::ZERO;
423 }
424
425 Length View::GetNumberOfGlyphs() const
426 {
427   if(mImpl->mVisualModel)
428   {
429     const VisualModel& model = *mImpl->mVisualModel;
430
431     const Length glyphCount    = model.mGlyphs.Count();
432     const Length positionCount = model.mGlyphPositions.Count();
433
434     DALI_ASSERT_DEBUG(positionCount <= glyphCount && "Invalid glyph positions in Model");
435
436     return (positionCount < glyphCount) ? positionCount : glyphCount;
437   }
438
439   return 0;
440 }
441
442 Length View::GetGlyphs(GlyphInfo* glyphs,
443                        Vector2*   glyphPositions,
444                        float&     minLineOffset,
445                        GlyphIndex glyphIndex,
446                        Length     numberOfGlyphs) const
447 {
448   Length                  numberOfLaidOutGlyphs       = 0u;
449   Length                  numberOfActualLaidOutGlyphs = 0u;
450   const float             modelCharacterSpacing       = mImpl->mVisualModel->GetCharacterSpacing();
451   Vector<CharacterIndex>& glyphToCharacterMap         = mImpl->mVisualModel->mGlyphsToCharacters;
452   const CharacterIndex*   glyphToCharacterMapBuffer   = glyphToCharacterMap.Begin();
453   float                   calculatedAdvance           = 0.f;
454   const Character*        textBuffer                  = mImpl->mLogicalModel->mText.Begin();
455
456   if(mImpl->mVisualModel)
457   {
458     // Get the character-spacing runs.
459     const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mImpl->mVisualModel->GetCharacterSpacingGlyphRuns();
460
461     bool                              textElided       = false;
462     DevelText::EllipsisPosition::Type ellipsisPosition = GetEllipsisPosition();
463
464     //Reset indices of ElidedGlyphs
465     mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
466     mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(numberOfGlyphs - 1u); // Initialization is the last index of Glyphs
467     mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
468     mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
469
470     // If ellipsis is enabled, the number of glyphs the layout engine has laid out may be less than 'numberOfGlyphs'.
471     // Check the last laid out line to know if the layout engine elided some text.
472
473     const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
474     if(numberOfLines > 0u)
475     {
476       const LineRun* const lines = mImpl->mVisualModel->mLines.Begin();
477
478       //Get line of ellipsis
479       const LineRun* ellipsisLine     = nullptr;
480       const LineRun* ellipsisNextLine = nullptr;
481       bool           hasEllipsis      = false;
482       for(Length lineIndex = 0; lineIndex < numberOfLines; lineIndex++)
483       {
484         const LineRun* line = (lines + lineIndex);
485         if(line->ellipsis)
486         {
487           ellipsisLine = line;
488           hasEllipsis  = true;
489           if(lineIndex < numberOfLines - 1u)
490           {
491             ellipsisNextLine = (lines + lineIndex + 1u);
492           }
493           break;
494         }
495       }
496
497       CalculateNumberOfLaidOutGlyphes(hasEllipsis, textElided, numberOfLaidOutGlyphs, numberOfActualLaidOutGlyphs, numberOfGlyphs, ellipsisPosition, ellipsisLine, numberOfLines, lines);
498
499       if(0u < numberOfActualLaidOutGlyphs)
500       {
501         // Retrieve from the visual model the glyphs and positions.
502         mImpl->mVisualModel->GetGlyphs(glyphs,
503                                        glyphIndex,
504                                        numberOfLaidOutGlyphs);
505
506         mImpl->mVisualModel->GetGlyphPositions(glyphPositions,
507                                                glyphIndex,
508                                                numberOfLaidOutGlyphs);
509
510         // Get the lines for the given range of glyphs.
511         // The lines contain the alignment offset which needs to be added to the glyph's position.
512         LineIndex firstLineIndex = 0u;
513         Length    numberOfLines  = 0u;
514         mImpl->mVisualModel->GetNumberOfLines(glyphIndex,
515                                               numberOfLaidOutGlyphs,
516                                               firstLineIndex,
517                                               numberOfLines);
518
519         Vector<LineRun> lines;
520         lines.Resize(numberOfLines);
521         LineRun* lineBuffer = lines.Begin();
522
523         mImpl->mVisualModel->GetLinesOfGlyphRange(lineBuffer,
524                                                   glyphIndex,
525                                                   numberOfLaidOutGlyphs);
526
527         // Get the first line for the given glyph range.
528         LineIndex lineIndex = firstLineIndex;
529         LineRun*  line      = lineBuffer + lineIndex;
530
531         // Index of the last glyph of the line.
532         GlyphIndex lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
533
534         // Add the alignment offset to the glyph's position.
535
536         minLineOffset = line->alignmentOffset;
537         float penY    = line->ascender;
538         for(Length index = 0u; index < numberOfLaidOutGlyphs; ++index)
539         {
540           Vector2& position = *(glyphPositions + index);
541           position.x += line->alignmentOffset;
542           position.y += penY;
543
544           if(lastGlyphIndexOfLine == index)
545           {
546             penY += -line->descender + line->lineSpacing;
547
548             // Get the next line.
549             ++lineIndex;
550
551             if(lineIndex < numberOfLines)
552             {
553               line          = lineBuffer + lineIndex;
554               minLineOffset = std::min(minLineOffset, line->alignmentOffset);
555
556               lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
557
558               penY += line->ascender;
559             }
560           }
561         }
562
563         // Set index where to set Ellipsis according to the selected position of Ellipsis.
564         // Start with this index to replace its glyph by Ellipsis, if the width  is not enough, then remove more glyphs.
565         GlyphIndex startIndexOfEllipsis = 0u;
566         if(hasEllipsis)
567         {
568           switch(ellipsisPosition)
569           {
570             case DevelText::EllipsisPosition::START:
571             {
572               // It's the fisrt glyph in line.
573               startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex;
574               break;
575             }
576             case DevelText::EllipsisPosition::MIDDLE:
577             {
578               // It's the second middle of the line in case the line split to two halves.
579               // Otherwise it's It's the last glyph in line (line before all removed lines).
580               startIndexOfEllipsis = ellipsisLine->isSplitToTwoHalves ? (ellipsisLine->glyphRunSecondHalf.glyphIndex) : (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u);
581               break;
582             }
583             case DevelText::EllipsisPosition::END:
584             {
585               // It's the last glyph in line.
586               startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u;
587               break;
588             }
589           }
590         }
591
592         if(1u == numberOfLaidOutGlyphs)
593         {
594           // not a point try to do ellipsis with only one laid out character.
595
596           return numberOfLaidOutGlyphs;
597         }
598
599         if(textElided)
600         {
601           const LineRun& elidedLine = *ellipsisLine;
602
603           if((1u == numberOfLines) &&
604              (GetLineHeight(elidedLine, true) > mImpl->mVisualModel->mControlSize.height))
605           {
606             // Replace the first glyph with ellipsis glyph
607             auto indexOfFirstGlyph = (ellipsisPosition == DevelText::EllipsisPosition::START) ? startIndexOfEllipsis : 0u;
608
609             // Regardless where the location of ellipsis,in-case the hight of line is greater than control's height
610             // then replace the first glyph with ellipsis glyph.
611
612             // Get the first glyph which is going to be replaced and the ellipsis glyph.
613             GlyphInfo&       glyphInfo     = *(glyphs + indexOfFirstGlyph);
614             const GlyphInfo& ellipsisGlyph = mImpl->mFontClient.GetEllipsisGlyph(mImpl->mFontClient.GetPointSize(glyphInfo.fontId));
615
616             // Change the 'x' and 'y' position of the ellipsis glyph.
617             Vector2& position = *(glyphPositions + indexOfFirstGlyph);
618             position.x        = ellipsisGlyph.xBearing;
619             position.y        = mImpl->mVisualModel->mControlSize.height - ellipsisGlyph.yBearing;
620
621             // Replace the glyph by the ellipsis glyph.
622             glyphInfo = ellipsisGlyph;
623
624             mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(indexOfFirstGlyph);
625             mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(indexOfFirstGlyph);
626             mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(indexOfFirstGlyph);
627             mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(indexOfFirstGlyph);
628
629             numberOfLaidOutGlyphs = 1u;
630
631             return numberOfLaidOutGlyphs;
632           }
633
634           // Add the ellipsis glyph.
635           Length     numberOfRemovedGlyphs = 0u;
636           GlyphIndex indexOfEllipsis       = startIndexOfEllipsis;
637
638           // Tail Mode: start by the end of line.
639           const bool isTailMode = ellipsisPosition == DevelText::EllipsisPosition::END ||
640                                   (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && numberOfLines != 1u);
641
642           // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed.
643           InsertEllipsisGlyph(
644             glyphs, indexOfEllipsis, numberOfRemovedGlyphs, glyphPositions, mImpl->mFontClient, characterSpacingGlyphRuns, modelCharacterSpacing,
645             calculatedAdvance, textBuffer, glyphToCharacterMapBuffer, numberOfGlyphs, isTailMode, ellipsisLine, numberOfLaidOutGlyphs);
646
647           RemoveAllGlyphsAfterEllipsisGlyph(
648               ellipsisPosition, numberOfLaidOutGlyphs, numberOfActualLaidOutGlyphs, numberOfRemovedGlyphs, isTailMode, indexOfEllipsis, ellipsisNextLine,
649               ellipsisLine, glyphs, glyphPositions, numberOfGlyphs, startIndexOfEllipsis, mImpl->mVisualModel);
650         }
651       }
652     }
653   }
654
655   return numberOfLaidOutGlyphs;
656 }
657
658 const Vector4* View::GetColors() const
659 {
660   if(mImpl->mVisualModel)
661   {
662     return mImpl->mVisualModel->mColors.Begin();
663   }
664
665   return NULL;
666 }
667
668 const ColorIndex* View::GetColorIndices() const
669 {
670   if(mImpl->mVisualModel)
671   {
672     return mImpl->mVisualModel->mColorIndices.Begin();
673   }
674
675   return NULL;
676 }
677
678 const Vector4* View::GetBackgroundColors() const
679 {
680   if(mImpl->mVisualModel)
681   {
682     return mImpl->mVisualModel->mBackgroundColors.Begin();
683   }
684
685   return nullptr;
686 }
687
688 const ColorIndex* View::GetBackgroundColorIndices() const
689 {
690   if(mImpl->mVisualModel)
691   {
692     return mImpl->mVisualModel->mBackgroundColorIndices.Begin();
693   }
694
695   return nullptr;
696 }
697
698 bool View::IsMarkupBackgroundColorSet() const
699 {
700   if(mImpl->mVisualModel)
701   {
702     return (mImpl->mVisualModel->mBackgroundColors.Count() > 0);
703   }
704
705   return false;
706 }
707
708 const Vector4& View::GetTextColor() const
709 {
710   if(mImpl->mVisualModel)
711   {
712     return mImpl->mVisualModel->GetTextColor();
713   }
714   return Vector4::ZERO;
715 }
716
717 const Vector2& View::GetShadowOffset() const
718 {
719   if(mImpl->mVisualModel)
720   {
721     return mImpl->mVisualModel->GetShadowOffset();
722   }
723   return Vector2::ZERO;
724 }
725
726 const Vector4& View::GetShadowColor() const
727 {
728   if(mImpl->mVisualModel)
729   {
730     return mImpl->mVisualModel->GetShadowColor();
731   }
732   return Vector4::ZERO;
733 }
734
735 const Vector4& View::GetUnderlineColor() const
736 {
737   if(mImpl->mVisualModel)
738   {
739     return mImpl->mVisualModel->GetUnderlineColor();
740   }
741   return Vector4::ZERO;
742 }
743
744 bool View::IsUnderlineEnabled() const
745 {
746   if(mImpl->mVisualModel)
747   {
748     return mImpl->mVisualModel->IsUnderlineEnabled();
749   }
750   return false;
751 }
752
753 bool View::IsMarkupUnderlineSet() const
754 {
755   return (GetNumberOfUnderlineRuns() > 0u);
756 }
757
758 const GlyphInfo* View::GetHyphens() const
759 {
760   if(mImpl->mVisualModel)
761   {
762     return mImpl->mVisualModel->mHyphen.glyph.Begin();
763   }
764
765   return nullptr;
766 }
767
768 const Length* View::GetHyphenIndices() const
769 {
770   if(mImpl->mVisualModel)
771   {
772     return mImpl->mVisualModel->mHyphen.index.Begin();
773   }
774
775   return nullptr;
776 }
777
778 Length View::GetHyphensCount() const
779 {
780   if(mImpl->mVisualModel)
781   {
782     return mImpl->mVisualModel->mHyphen.glyph.Size();
783   }
784
785   return 0;
786 }
787 float View::GetUnderlineHeight() const
788 {
789   if(mImpl->mVisualModel)
790   {
791     return mImpl->mVisualModel->GetUnderlineHeight();
792   }
793   return 0.0f;
794 }
795
796 Text::Underline::Type View::GetUnderlineType() const
797 {
798   Text::Underline::Type type = Text::Underline::Type::SOLID;
799   if(mImpl->mVisualModel)
800   {
801     type = mImpl->mVisualModel->GetUnderlineType();
802   }
803   return type;
804 }
805
806 float View::GetDashedUnderlineWidth() const
807 {
808   float width = 0.0f;
809   if(mImpl->mVisualModel)
810   {
811     width = mImpl->mVisualModel->GetDashedUnderlineWidth();
812   }
813   return width;
814 }
815
816 float View::GetDashedUnderlineGap() const
817 {
818   float gap = 0.0f;
819   if(mImpl->mVisualModel)
820   {
821     gap = mImpl->mVisualModel->GetDashedUnderlineGap();
822   }
823   return gap;
824 }
825
826 Length View::GetNumberOfUnderlineRuns() const
827 {
828   if(mImpl->mVisualModel)
829   {
830     return mImpl->mVisualModel->GetNumberOfUnderlineRuns();
831   }
832
833   return 0u;
834 }
835
836 void View::GetUnderlineRuns(UnderlinedGlyphRun* underlineRuns,
837                             UnderlineRunIndex   index,
838                             Length              numberOfRuns) const
839 {
840   if(mImpl->mVisualModel)
841   {
842     mImpl->mVisualModel->GetUnderlineRuns(underlineRuns,
843                                           index,
844                                           numberOfRuns);
845   }
846 }
847
848 const Vector4& View::GetOutlineColor() const
849 {
850   if(mImpl->mVisualModel)
851   {
852     return mImpl->mVisualModel->GetOutlineColor();
853   }
854   return Vector4::ZERO;
855 }
856
857 uint16_t View::GetOutlineWidth() const
858 {
859   if(mImpl->mVisualModel)
860   {
861     return mImpl->mVisualModel->GetOutlineWidth();
862   }
863   return 0u;
864 }
865
866 DevelText::EllipsisPosition::Type View::GetEllipsisPosition() const
867 {
868   DevelText::EllipsisPosition::Type ellipsisPosition = DevelText::EllipsisPosition::END;
869   if(mImpl->mVisualModel)
870   {
871     const VisualModel& model = *mImpl->mVisualModel;
872     ellipsisPosition         = model.GetEllipsisPosition();
873   }
874
875   return ellipsisPosition;
876 }
877
878 bool View::IsTextElideEnabled() const
879 {
880   bool isTextElideEnabled = false;
881
882   if(mImpl->mVisualModel)
883   {
884     const VisualModel& model = *mImpl->mVisualModel;
885     isTextElideEnabled       = model.IsTextElideEnabled();
886   }
887
888   return isTextElideEnabled;
889 }
890
891 GlyphIndex View::GetStartIndexOfElidedGlyphs() const
892 {
893   GlyphIndex startIndexOfElidedGlyphs = 0u;
894
895   if(mImpl->mVisualModel)
896   {
897     const VisualModel& model = *mImpl->mVisualModel;
898     startIndexOfElidedGlyphs = model.GetStartIndexOfElidedGlyphs();
899   }
900
901   return startIndexOfElidedGlyphs;
902 }
903
904 GlyphIndex View::GetEndIndexOfElidedGlyphs() const
905 {
906   GlyphIndex endIndexOfElidedGlyphs = 0u;
907
908   if(mImpl->mVisualModel)
909   {
910     const VisualModel& model = *mImpl->mVisualModel;
911     endIndexOfElidedGlyphs   = model.GetEndIndexOfElidedGlyphs();
912   }
913
914   return endIndexOfElidedGlyphs;
915 }
916
917 GlyphIndex View::GetFirstMiddleIndexOfElidedGlyphs() const
918 {
919   GlyphIndex firstMiddleIndexOfElidedGlyphs = 0u;
920
921   if(mImpl->mVisualModel)
922   {
923     const VisualModel& model       = *mImpl->mVisualModel;
924     firstMiddleIndexOfElidedGlyphs = model.GetFirstMiddleIndexOfElidedGlyphs();
925   }
926
927   return firstMiddleIndexOfElidedGlyphs;
928 }
929
930 GlyphIndex View::GetSecondMiddleIndexOfElidedGlyphs() const
931 {
932   GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
933
934   if(mImpl->mVisualModel)
935   {
936     const VisualModel& model        = *mImpl->mVisualModel;
937     secondMiddleIndexOfElidedGlyphs = model.GetSecondMiddleIndexOfElidedGlyphs();
938   }
939
940   return secondMiddleIndexOfElidedGlyphs;
941 }
942
943 const Vector4& View::GetStrikethroughColor() const
944 {
945   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughColor() : Vector4::ZERO;
946 }
947
948 bool View::IsStrikethroughEnabled() const
949 {
950   return (mImpl->mVisualModel) ? mImpl->mVisualModel->IsStrikethroughEnabled() : false;
951 }
952
953 bool View::IsMarkupStrikethroughSet() const
954 {
955   return (GetNumberOfStrikethroughRuns() > 0u);
956 }
957
958 float View::GetStrikethroughHeight() const
959 {
960   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughHeight() : 0.0f;
961 }
962
963 Length View::GetNumberOfStrikethroughRuns() const
964 {
965   if(mImpl->mVisualModel)
966   {
967     return mImpl->mVisualModel->GetNumberOfStrikethroughRuns();
968   }
969
970   return 0u;
971 }
972
973 void View::GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns,
974                                 StrikethroughRunIndex  index,
975                                 Length                 numberOfRuns) const
976 {
977   if(mImpl->mVisualModel)
978   {
979     mImpl->mVisualModel->GetStrikethroughRuns(strikethroughRuns,
980                                               index,
981                                               numberOfRuns);
982   }
983 }
984
985 Length View::GetNumberOfBoundedParagraphRuns() const
986 {
987   if(mImpl->mLogicalModel)
988   {
989     return mImpl->mLogicalModel->GetNumberOfBoundedParagraphRuns();
990   }
991
992   return 0u;
993 }
994
995 const Vector<BoundedParagraphRun>& View::GetBoundedParagraphRuns() const
996 {
997   return mImpl->mLogicalModel->GetBoundedParagraphRuns();
998 }
999
1000 float View::GetCharacterSpacing() const
1001 {
1002   return mImpl->mVisualModel->GetCharacterSpacing();
1003 }
1004
1005 const Character* View::GetTextBuffer() const
1006 {
1007   return mImpl->mLogicalModel->mText.Begin();
1008 }
1009
1010 const Vector<CharacterIndex>& View::GetGlyphsToCharacters() const
1011 {
1012   return mImpl->mVisualModel->GetGlyphsToCharacters();
1013 }
1014
1015 } // namespace Dali::Toolkit::Text