[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) 2022 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               //Copy elided glyphs after the ellipsis glyph.
553               memcpy(glyphs + firstMiddleIndexOfElidedGlyphs + 1u, glyphs + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo));
554               memcpy(glyphPositions + firstMiddleIndexOfElidedGlyphs + 1u, glyphPositions + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2));
555             }
556           }
557           else if(ellipsisPosition == DevelText::EllipsisPosition::START)
558           {
559             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
560             //Copy elided glyphs after the ellipsis glyph.
561             memcpy(glyphs, glyphs + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(GlyphInfo));
562             memcpy(glyphPositions, glyphPositions + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(Vector2));
563             mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(indexOfEllipsis);
564           }
565           else // DevelText::EllipsisPosition::END
566           {
567             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
568             mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(indexOfEllipsis);
569           }
570         }
571       }
572     }
573   }
574
575   return numberOfLaidOutGlyphs;
576 }
577
578 const Vector4* View::GetColors() const
579 {
580   if(mImpl->mVisualModel)
581   {
582     return mImpl->mVisualModel->mColors.Begin();
583   }
584
585   return NULL;
586 }
587
588 const ColorIndex* View::GetColorIndices() const
589 {
590   if(mImpl->mVisualModel)
591   {
592     return mImpl->mVisualModel->mColorIndices.Begin();
593   }
594
595   return NULL;
596 }
597
598 const Vector4* View::GetBackgroundColors() const
599 {
600   if(mImpl->mVisualModel)
601   {
602     return mImpl->mVisualModel->mBackgroundColors.Begin();
603   }
604
605   return nullptr;
606 }
607
608 const ColorIndex* View::GetBackgroundColorIndices() const
609 {
610   if(mImpl->mVisualModel)
611   {
612     return mImpl->mVisualModel->mBackgroundColorIndices.Begin();
613   }
614
615   return nullptr;
616 }
617
618 bool View::IsMarkupBackgroundColorSet() const
619 {
620   if(mImpl->mVisualModel)
621   {
622     return (mImpl->mVisualModel->mBackgroundColors.Count() > 0);
623   }
624
625   return false;
626 }
627
628 const Vector4& View::GetTextColor() const
629 {
630   if(mImpl->mVisualModel)
631   {
632     return mImpl->mVisualModel->GetTextColor();
633   }
634   return Vector4::ZERO;
635 }
636
637 const Vector2& View::GetShadowOffset() const
638 {
639   if(mImpl->mVisualModel)
640   {
641     return mImpl->mVisualModel->GetShadowOffset();
642   }
643   return Vector2::ZERO;
644 }
645
646 const Vector4& View::GetShadowColor() const
647 {
648   if(mImpl->mVisualModel)
649   {
650     return mImpl->mVisualModel->GetShadowColor();
651   }
652   return Vector4::ZERO;
653 }
654
655 const Vector4& View::GetUnderlineColor() const
656 {
657   if(mImpl->mVisualModel)
658   {
659     return mImpl->mVisualModel->GetUnderlineColor();
660   }
661   return Vector4::ZERO;
662 }
663
664 bool View::IsUnderlineEnabled() const
665 {
666   if(mImpl->mVisualModel)
667   {
668     return mImpl->mVisualModel->IsUnderlineEnabled();
669   }
670   return false;
671 }
672
673 bool View::IsMarkupUnderlineSet() const
674 {
675   return (GetNumberOfUnderlineRuns() > 0u);
676 }
677
678 const GlyphInfo* View::GetHyphens() const
679 {
680   if(mImpl->mVisualModel)
681   {
682     return mImpl->mVisualModel->mHyphen.glyph.Begin();
683   }
684
685   return nullptr;
686 }
687
688 const Length* View::GetHyphenIndices() const
689 {
690   if(mImpl->mVisualModel)
691   {
692     return mImpl->mVisualModel->mHyphen.index.Begin();
693   }
694
695   return nullptr;
696 }
697
698 Length View::GetHyphensCount() const
699 {
700   if(mImpl->mVisualModel)
701   {
702     return mImpl->mVisualModel->mHyphen.glyph.Size();
703   }
704
705   return 0;
706 }
707 float View::GetUnderlineHeight() const
708 {
709   if(mImpl->mVisualModel)
710   {
711     return mImpl->mVisualModel->GetUnderlineHeight();
712   }
713   return 0.0f;
714 }
715
716 Text::Underline::Type View::GetUnderlineType() const
717 {
718   Text::Underline::Type type = Text::Underline::Type::SOLID;
719   if(mImpl->mVisualModel)
720   {
721     type = mImpl->mVisualModel->GetUnderlineType();
722   }
723   return type;
724 }
725
726 float View::GetDashedUnderlineWidth() const
727 {
728   float width = 0.0f;
729   if(mImpl->mVisualModel)
730   {
731     width = mImpl->mVisualModel->GetDashedUnderlineWidth();
732   }
733   return width;
734 }
735
736 float View::GetDashedUnderlineGap() const
737 {
738   float gap = 0.0f;
739   if(mImpl->mVisualModel)
740   {
741     gap = mImpl->mVisualModel->GetDashedUnderlineGap();
742   }
743   return gap;
744 }
745
746 Length View::GetNumberOfUnderlineRuns() const
747 {
748   if(mImpl->mVisualModel)
749   {
750     return mImpl->mVisualModel->GetNumberOfUnderlineRuns();
751   }
752
753   return 0u;
754 }
755
756 void View::GetUnderlineRuns(UnderlinedGlyphRun* underlineRuns,
757                             UnderlineRunIndex   index,
758                             Length              numberOfRuns) const
759 {
760   if(mImpl->mVisualModel)
761   {
762     mImpl->mVisualModel->GetUnderlineRuns(underlineRuns,
763                                           index,
764                                           numberOfRuns);
765   }
766 }
767
768 const Vector4& View::GetOutlineColor() const
769 {
770   if(mImpl->mVisualModel)
771   {
772     return mImpl->mVisualModel->GetOutlineColor();
773   }
774   return Vector4::ZERO;
775 }
776
777 uint16_t View::GetOutlineWidth() const
778 {
779   if(mImpl->mVisualModel)
780   {
781     return mImpl->mVisualModel->GetOutlineWidth();
782   }
783   return 0u;
784 }
785
786 DevelText::EllipsisPosition::Type View::GetEllipsisPosition() const
787 {
788   DevelText::EllipsisPosition::Type ellipsisPosition = DevelText::EllipsisPosition::END;
789   if(mImpl->mVisualModel)
790   {
791     const VisualModel& model = *mImpl->mVisualModel;
792     ellipsisPosition         = model.GetEllipsisPosition();
793   }
794
795   return ellipsisPosition;
796 }
797
798 bool View::IsTextElideEnabled() const
799 {
800   bool isTextElideEnabled = false;
801
802   if(mImpl->mVisualModel)
803   {
804     const VisualModel& model = *mImpl->mVisualModel;
805     isTextElideEnabled       = model.IsTextElideEnabled();
806   }
807
808   return isTextElideEnabled;
809 }
810
811 GlyphIndex View::GetStartIndexOfElidedGlyphs() const
812 {
813   GlyphIndex startIndexOfElidedGlyphs = 0u;
814
815   if(mImpl->mVisualModel)
816   {
817     const VisualModel& model = *mImpl->mVisualModel;
818     startIndexOfElidedGlyphs = model.GetStartIndexOfElidedGlyphs();
819   }
820
821   return startIndexOfElidedGlyphs;
822 }
823
824 GlyphIndex View::GetEndIndexOfElidedGlyphs() const
825 {
826   GlyphIndex endIndexOfElidedGlyphs = 0u;
827
828   if(mImpl->mVisualModel)
829   {
830     const VisualModel& model = *mImpl->mVisualModel;
831     endIndexOfElidedGlyphs   = model.GetEndIndexOfElidedGlyphs();
832   }
833
834   return endIndexOfElidedGlyphs;
835 }
836
837 GlyphIndex View::GetFirstMiddleIndexOfElidedGlyphs() const
838 {
839   GlyphIndex firstMiddleIndexOfElidedGlyphs = 0u;
840
841   if(mImpl->mVisualModel)
842   {
843     const VisualModel& model       = *mImpl->mVisualModel;
844     firstMiddleIndexOfElidedGlyphs = model.GetFirstMiddleIndexOfElidedGlyphs();
845   }
846
847   return firstMiddleIndexOfElidedGlyphs;
848 }
849
850 GlyphIndex View::GetSecondMiddleIndexOfElidedGlyphs() const
851 {
852   GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
853
854   if(mImpl->mVisualModel)
855   {
856     const VisualModel& model        = *mImpl->mVisualModel;
857     secondMiddleIndexOfElidedGlyphs = model.GetSecondMiddleIndexOfElidedGlyphs();
858   }
859
860   return secondMiddleIndexOfElidedGlyphs;
861 }
862
863 const Vector4& View::GetStrikethroughColor() const
864 {
865   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughColor() : Vector4::ZERO;
866 }
867
868 bool View::IsStrikethroughEnabled() const
869 {
870   return (mImpl->mVisualModel) ? mImpl->mVisualModel->IsStrikethroughEnabled() : false;
871 }
872
873 bool View::IsMarkupStrikethroughSet() const
874 {
875   return (GetNumberOfStrikethroughRuns() > 0u);
876 }
877
878 float View::GetStrikethroughHeight() const
879 {
880   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughHeight() : 0.0f;
881 }
882
883 Length View::GetNumberOfStrikethroughRuns() const
884 {
885   if(mImpl->mVisualModel)
886   {
887     return mImpl->mVisualModel->GetNumberOfStrikethroughRuns();
888   }
889
890   return 0u;
891 }
892
893 void View::GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns,
894                                 StrikethroughRunIndex  index,
895                                 Length                 numberOfRuns) const
896 {
897   if(mImpl->mVisualModel)
898   {
899     mImpl->mVisualModel->GetStrikethroughRuns(strikethroughRuns,
900                                               index,
901                                               numberOfRuns);
902   }
903 }
904
905 Length View::GetNumberOfBoundedParagraphRuns() const
906 {
907   if(mImpl->mLogicalModel)
908   {
909     return mImpl->mLogicalModel->GetNumberOfBoundedParagraphRuns();
910   }
911
912   return 0u;
913 }
914
915 const Vector<BoundedParagraphRun>& View::GetBoundedParagraphRuns() const
916 {
917   return mImpl->mLogicalModel->GetBoundedParagraphRuns();
918 }
919
920 Length View::GetNumberOfCharacterSpacingGlyphRuns() const
921 {
922   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetNumberOfCharacterSpacingGlyphRuns() : 0u;
923 }
924
925 const Vector<CharacterSpacingGlyphRun>& View::GetCharacterSpacingGlyphRuns() const
926 {
927   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetCharacterSpacingGlyphRuns() : GetEmptyCharacterSpacingGlyphRuns();
928 }
929
930 float View::GetCharacterSpacing() const
931 {
932   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetCharacterSpacing() : 0.f;
933 }
934
935 const Character* View::GetTextBuffer() const
936 {
937   return (mImpl->mVisualModel) ? mImpl->mLogicalModel->mText.Begin() : nullptr;
938 }
939
940 const Vector<CharacterIndex>& View::GetGlyphsToCharacters() const
941 {
942   return mImpl->mVisualModel->GetGlyphsToCharacters();
943 }
944
945 } // namespace Text
946
947 } // namespace Toolkit
948
949 } // namespace Dali