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