[dali_2.1.30] 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* const 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* const 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* const 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* const View::GetBackgroundColorIndices() const
609 {
610   if(mImpl->mVisualModel)
611   {
612     return mImpl->mVisualModel->mBackgroundColorIndices.Begin();
613   }
614
615   return nullptr;
616 }
617
618 bool const 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 const GlyphInfo* View::GetHyphens() const
674 {
675   if(mImpl->mVisualModel)
676   {
677     return mImpl->mVisualModel->mHyphen.glyph.Begin();
678   }
679
680   return nullptr;
681 }
682
683 const Length* View::GetHyphenIndices() const
684 {
685   if(mImpl->mVisualModel)
686   {
687     return mImpl->mVisualModel->mHyphen.index.Begin();
688   }
689
690   return nullptr;
691 }
692
693 Length View::GetHyphensCount() const
694 {
695   if(mImpl->mVisualModel)
696   {
697     return mImpl->mVisualModel->mHyphen.glyph.Size();
698   }
699
700   return 0;
701 }
702 float View::GetUnderlineHeight() const
703 {
704   if(mImpl->mVisualModel)
705   {
706     return mImpl->mVisualModel->GetUnderlineHeight();
707   }
708   return 0.0f;
709 }
710
711 Text::Underline::Type View::GetUnderlineType() const
712 {
713   Text::Underline::Type type = Text::Underline::Type::SOLID;
714   if(mImpl->mVisualModel)
715   {
716     type = mImpl->mVisualModel->GetUnderlineType();
717   }
718   return type;
719 }
720
721 float View::GetDashedUnderlineWidth() const
722 {
723   float width = 0.0f;
724   if(mImpl->mVisualModel)
725   {
726     width = mImpl->mVisualModel->GetDashedUnderlineWidth();
727   }
728   return width;
729 }
730
731 float View::GetDashedUnderlineGap() const
732 {
733   float gap = 0.0f;
734   if(mImpl->mVisualModel)
735   {
736     gap = mImpl->mVisualModel->GetDashedUnderlineGap();
737   }
738   return gap;
739 }
740
741 Length View::GetNumberOfUnderlineRuns() const
742 {
743   if(mImpl->mVisualModel)
744   {
745     return mImpl->mVisualModel->GetNumberOfUnderlineRuns();
746   }
747
748   return 0u;
749 }
750
751 void View::GetUnderlineRuns(UnderlinedGlyphRun* underlineRuns,
752                             UnderlineRunIndex   index,
753                             Length              numberOfRuns) const
754 {
755   if(mImpl->mVisualModel)
756   {
757     mImpl->mVisualModel->GetUnderlineRuns(underlineRuns,
758                                           index,
759                                           numberOfRuns);
760   }
761 }
762
763 const Vector4& View::GetOutlineColor() const
764 {
765   if(mImpl->mVisualModel)
766   {
767     return mImpl->mVisualModel->GetOutlineColor();
768   }
769   return Vector4::ZERO;
770 }
771
772 uint16_t View::GetOutlineWidth() const
773 {
774   if(mImpl->mVisualModel)
775   {
776     return mImpl->mVisualModel->GetOutlineWidth();
777   }
778   return 0u;
779 }
780
781 DevelText::EllipsisPosition::Type View::GetEllipsisPosition() const
782 {
783   DevelText::EllipsisPosition::Type ellipsisPosition = DevelText::EllipsisPosition::END;
784   if(mImpl->mVisualModel)
785   {
786     const VisualModel& model = *mImpl->mVisualModel;
787     ellipsisPosition         = model.GetEllipsisPosition();
788   }
789
790   return ellipsisPosition;
791 }
792
793 bool View::IsTextElideEnabled() const
794 {
795   bool isTextElideEnabled = false;
796
797   if(mImpl->mVisualModel)
798   {
799     const VisualModel& model = *mImpl->mVisualModel;
800     isTextElideEnabled       = model.IsTextElideEnabled();
801   }
802
803   return isTextElideEnabled;
804 }
805
806 GlyphIndex View::GetStartIndexOfElidedGlyphs() const
807 {
808   GlyphIndex startIndexOfElidedGlyphs = 0u;
809
810   if(mImpl->mVisualModel)
811   {
812     const VisualModel& model = *mImpl->mVisualModel;
813     startIndexOfElidedGlyphs = model.GetStartIndexOfElidedGlyphs();
814   }
815
816   return startIndexOfElidedGlyphs;
817 }
818
819 GlyphIndex View::GetEndIndexOfElidedGlyphs() const
820 {
821   GlyphIndex endIndexOfElidedGlyphs = 0u;
822
823   if(mImpl->mVisualModel)
824   {
825     const VisualModel& model = *mImpl->mVisualModel;
826     endIndexOfElidedGlyphs   = model.GetEndIndexOfElidedGlyphs();
827   }
828
829   return endIndexOfElidedGlyphs;
830 }
831
832 GlyphIndex View::GetFirstMiddleIndexOfElidedGlyphs() const
833 {
834   GlyphIndex firstMiddleIndexOfElidedGlyphs = 0u;
835
836   if(mImpl->mVisualModel)
837   {
838     const VisualModel& model       = *mImpl->mVisualModel;
839     firstMiddleIndexOfElidedGlyphs = model.GetFirstMiddleIndexOfElidedGlyphs();
840   }
841
842   return firstMiddleIndexOfElidedGlyphs;
843 }
844
845 GlyphIndex View::GetSecondMiddleIndexOfElidedGlyphs() const
846 {
847   GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
848
849   if(mImpl->mVisualModel)
850   {
851     const VisualModel& model        = *mImpl->mVisualModel;
852     secondMiddleIndexOfElidedGlyphs = model.GetSecondMiddleIndexOfElidedGlyphs();
853   }
854
855   return secondMiddleIndexOfElidedGlyphs;
856 }
857
858 const Vector4& View::GetStrikethroughColor() const
859 {
860   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughColor() : Vector4::ZERO;
861 }
862
863 bool View::IsStrikethroughEnabled() const
864 {
865   return (mImpl->mVisualModel) ? mImpl->mVisualModel->IsStrikethroughEnabled() : false;
866 }
867
868 float View::GetStrikethroughHeight() const
869 {
870   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughHeight() : 0.0f;
871 }
872
873 Length View::GetNumberOfStrikethroughRuns() const
874 {
875   if(mImpl->mVisualModel)
876   {
877     return mImpl->mVisualModel->GetNumberOfStrikethroughRuns();
878   }
879
880   return 0u;
881 }
882
883 void View::GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns,
884                                 StrikethroughRunIndex  index,
885                                 Length                 numberOfRuns) const
886 {
887   if(mImpl->mVisualModel)
888   {
889     mImpl->mVisualModel->GetStrikethroughRuns(strikethroughRuns,
890                                               index,
891                                               numberOfRuns);
892   }
893 }
894
895 Length View::GetNumberOfBoundedParagraphRuns() const
896 {
897   if(mImpl->mLogicalModel)
898   {
899     return mImpl->mLogicalModel->GetNumberOfBoundedParagraphRuns();
900   }
901
902   return 0u;
903 }
904
905 const Vector<BoundedParagraphRun>& View::GetBoundedParagraphRuns() const
906 {
907   return mImpl->mLogicalModel->GetBoundedParagraphRuns();
908 }
909
910 Length View::GetNumberOfCharacterSpacingGlyphRuns() const
911 {
912   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetNumberOfCharacterSpacingGlyphRuns() : 0u;
913 }
914
915 const Vector<CharacterSpacingGlyphRun>& View::GetCharacterSpacingGlyphRuns() const
916 {
917   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetCharacterSpacingGlyphRuns() : GetEmptyCharacterSpacingGlyphRuns();
918 }
919
920 const float View::GetCharacterSpacing() const
921 {
922   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetCharacterSpacing() : 0.f;
923 }
924
925 const Character* View::GetTextBuffer() const
926 {
927   return (mImpl->mVisualModel) ? mImpl->mLogicalModel->mText.Begin() : nullptr;
928 }
929
930 const Vector<CharacterIndex>& View::GetGlyphsToCharacters() const
931 {
932   return mImpl->mVisualModel->GetGlyphsToCharacters();
933 }
934
935 } // namespace Text
936
937 } // namespace Toolkit
938
939 } // namespace Dali