Merge "Add ENABLE_FONT_SIZE_SCALE property to text components" into 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 namespace Dali
27 {
28 namespace Toolkit
29 {
30 namespace Text
31 {
32 struct View::Impl
33 {
34   VisualModelPtr              mVisualModel;
35   TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
36 };
37
38 View::View()
39 : mImpl(NULL)
40 {
41   mImpl = new View::Impl();
42
43   mImpl->mFontClient = TextAbstraction::FontClient::Get();
44 }
45
46 View::~View()
47 {
48   delete mImpl;
49 }
50
51 void View::SetVisualModel(VisualModelPtr visualModel)
52 {
53   mImpl->mVisualModel = visualModel;
54 }
55
56 const Vector2& View::GetControlSize() const
57 {
58   if(mImpl->mVisualModel)
59   {
60     return mImpl->mVisualModel->mControlSize;
61   }
62
63   return Vector2::ZERO;
64 }
65
66 const Vector2& View::GetLayoutSize() const
67 {
68   if(mImpl->mVisualModel)
69   {
70     return mImpl->mVisualModel->GetLayoutSize();
71   }
72
73   return Vector2::ZERO;
74 }
75
76 Length View::GetNumberOfGlyphs() const
77 {
78   if(mImpl->mVisualModel)
79   {
80     const VisualModel& model = *mImpl->mVisualModel;
81
82     const Length glyphCount    = model.mGlyphs.Count();
83     const Length positionCount = model.mGlyphPositions.Count();
84
85     DALI_ASSERT_DEBUG(positionCount <= glyphCount && "Invalid glyph positions in Model");
86
87     return (positionCount < glyphCount) ? positionCount : glyphCount;
88   }
89
90   return 0;
91 }
92
93 Length View::GetGlyphs(GlyphInfo* glyphs,
94                        Vector2*   glyphPositions,
95                        float&     minLineOffset,
96                        GlyphIndex glyphIndex,
97                        Length     numberOfGlyphs) const
98 {
99   Length numberOfLaidOutGlyphs       = 0u;
100   Length numberOfActualLaidOutGlyphs = 0u;
101
102   if(mImpl->mVisualModel)
103   {
104     bool                              textElided       = false;
105     DevelText::EllipsisPosition::Type ellipsisPosition = GetEllipsisPosition();
106
107     //Reset indices of ElidedGlyphs
108     mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
109     mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(numberOfGlyphs);
110     mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
111     mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
112
113     // If ellipsis is enabled, the number of glyphs the layout engine has laid out may be less than 'numberOfGlyphs'.
114     // Check the last laid out line to know if the layout engine elided some text.
115
116     const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
117     if(numberOfLines > 0u)
118     {
119       const LineRun* const lines = mImpl->mVisualModel->mLines.Begin();
120
121       //Get line of ellipsis
122       const LineRun* ellipsisLine     = nullptr;
123       const LineRun* ellipsisNextLine = nullptr;
124       bool           hasEllipsis      = false;
125       for(Length lineIndex = 0; lineIndex < numberOfLines; lineIndex++)
126       {
127         const LineRun* line = (lines + lineIndex);
128         if(line->ellipsis)
129         {
130           ellipsisLine = line;
131           hasEllipsis  = true;
132           if(lineIndex < numberOfLines - 1u)
133           {
134             ellipsisNextLine = (lines + lineIndex + 1u);
135           }
136           break;
137         }
138       }
139
140       // If ellipsis is enabled, calculate the number of laid out glyphs.
141       // Otherwise use the given number of glyphs.
142       if(hasEllipsis)
143       {
144         textElided            = true;
145         numberOfLaidOutGlyphs = numberOfGlyphs;
146
147         if(ellipsisPosition == DevelText::EllipsisPosition::START)
148         {
149           numberOfActualLaidOutGlyphs = numberOfGlyphs - ellipsisLine->glyphRun.glyphIndex;
150         }
151         else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
152         {
153           numberOfActualLaidOutGlyphs = 0u;
154           for(Length lineIndex = 0u; lineIndex < numberOfLines; lineIndex++)
155           {
156             numberOfActualLaidOutGlyphs += lines[lineIndex].glyphRun.numberOfGlyphs + lines[lineIndex].glyphRunSecondHalf.numberOfGlyphs;
157           }
158         }
159         else // DevelText::EllipsisPosition::END
160         {
161           numberOfActualLaidOutGlyphs = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs;
162         }
163       }
164       else
165       {
166         numberOfActualLaidOutGlyphs = numberOfLaidOutGlyphs = numberOfGlyphs;
167       }
168
169       if(0u < numberOfActualLaidOutGlyphs)
170       {
171         // Retrieve from the visual model the glyphs and positions.
172         mImpl->mVisualModel->GetGlyphs(glyphs,
173                                        glyphIndex,
174                                        numberOfLaidOutGlyphs);
175
176         mImpl->mVisualModel->GetGlyphPositions(glyphPositions,
177                                                glyphIndex,
178                                                numberOfLaidOutGlyphs);
179
180         // Get the lines for the given range of glyphs.
181         // The lines contain the alignment offset which needs to be added to the glyph's position.
182         LineIndex firstLineIndex = 0u;
183         Length    numberOfLines  = 0u;
184         mImpl->mVisualModel->GetNumberOfLines(glyphIndex,
185                                               numberOfLaidOutGlyphs,
186                                               firstLineIndex,
187                                               numberOfLines);
188
189         Vector<LineRun> lines;
190         lines.Resize(numberOfLines);
191         LineRun* lineBuffer = lines.Begin();
192
193         mImpl->mVisualModel->GetLinesOfGlyphRange(lineBuffer,
194                                                   glyphIndex,
195                                                   numberOfLaidOutGlyphs);
196
197         // Get the first line for the given glyph range.
198         LineIndex lineIndex = firstLineIndex;
199         LineRun*  line      = lineBuffer + lineIndex;
200
201         // Index of the last glyph of the line.
202         GlyphIndex lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
203
204         // Add the alignment offset to the glyph's position.
205
206         minLineOffset = line->alignmentOffset;
207         float penY    = line->ascender;
208         for(Length index = 0u; index < numberOfLaidOutGlyphs; ++index)
209         {
210           Vector2& position = *(glyphPositions + index);
211           position.x += line->alignmentOffset;
212           position.y += penY;
213
214           if(lastGlyphIndexOfLine == index)
215           {
216             penY += -line->descender + line->lineSpacing;
217
218             // Get the next line.
219             ++lineIndex;
220
221             if(lineIndex < numberOfLines)
222             {
223               line          = lineBuffer + lineIndex;
224               minLineOffset = std::min(minLineOffset, line->alignmentOffset);
225
226               lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
227
228               penY += line->ascender;
229             }
230           }
231         }
232
233         // Set index where to set Ellipsis according to the selected position of Ellipsis.
234         // Start with this index to replace its glyph by Ellipsis, if the width  is not enough, then remove more glyphs.
235         GlyphIndex startIndexOfEllipsis = 0u;
236         if(hasEllipsis)
237         {
238           if(ellipsisPosition == DevelText::EllipsisPosition::START)
239           {
240             // It's the fisrt glyph in line.
241             startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex;
242           }
243           else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
244           {
245             // It's the second middle of the line in case the line split to two halves.
246             // Otherwise it's It's the last glyph in line (line before all removed lines).
247             startIndexOfEllipsis = ellipsisLine->isSplitToTwoHalves ? (ellipsisLine->glyphRunSecondHalf.glyphIndex) : (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u);
248           }
249           else // DevelText::EllipsisPosition::END
250           {
251             // It's the last glyph in line.
252             startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u;
253           }
254         }
255
256         if(1u == numberOfLaidOutGlyphs)
257         {
258           // not a point try to do ellipsis with only one laid out character.
259
260           return numberOfLaidOutGlyphs;
261         }
262
263         if(textElided)
264         {
265           const LineRun& elidedLine = *ellipsisLine;
266
267           if((1u == numberOfLines) &&
268              (GetLineHeight(elidedLine) > mImpl->mVisualModel->mControlSize.height))
269           {
270             // Replace the first glyph with ellipsis glyph
271             auto indexOfFirstGlyph = (ellipsisPosition == DevelText::EllipsisPosition::START) ? startIndexOfEllipsis : 0u;
272
273             // Regardless where the location of ellipsis,in-case the hight of line is greater than control's height
274             // then replace the first glyph with ellipsis glyph.
275
276             // Get the first glyph which is going to be replaced and the ellipsis glyph.
277             GlyphInfo&       glyphInfo     = *(glyphs + indexOfFirstGlyph);
278             const GlyphInfo& ellipsisGlyph = mImpl->mFontClient.GetEllipsisGlyph(mImpl->mFontClient.GetPointSize(glyphInfo.fontId));
279
280             // Change the 'x' and 'y' position of the ellipsis glyph.
281             Vector2& position = *(glyphPositions + indexOfFirstGlyph);
282             position.x        = ellipsisGlyph.xBearing;
283             position.y        = mImpl->mVisualModel->mControlSize.height - ellipsisGlyph.yBearing;
284
285             // Replace the glyph by the ellipsis glyph.
286             glyphInfo = ellipsisGlyph;
287
288             mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(indexOfFirstGlyph);
289             mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(indexOfFirstGlyph);
290             mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(indexOfFirstGlyph);
291             mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(indexOfFirstGlyph);
292
293             numberOfLaidOutGlyphs = 1u;
294
295             return numberOfLaidOutGlyphs;
296           }
297
298           // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
299           float firstPenX   = 0.f; // Used if rtl text is elided.
300           float penY        = 0.f;
301           bool  firstPenSet = false;
302
303           // Add the ellipsis glyph.
304           bool       inserted              = false;
305           float      removedGlypsWidth     = 0.f;
306           Length     numberOfRemovedGlyphs = 0u;
307           GlyphIndex indexOfEllipsis       = startIndexOfEllipsis;
308
309           // Tail Mode: start by the end of line.
310           const bool isTailMode = ellipsisPosition == DevelText::EllipsisPosition::END ||
311                                   (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && numberOfLines != 1u);
312
313           // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed.
314           while(!inserted)
315           {
316             const GlyphInfo& glyphToRemove = *(glyphs + indexOfEllipsis);
317
318             if(0u != glyphToRemove.fontId)
319             {
320               // i.e. The font id of the glyph shaped from the '\n' character is zero.
321
322               // Need to reshape the glyph as the font may be different in size.
323               const GlyphInfo& ellipsisGlyph = mImpl->mFontClient.GetEllipsisGlyph(mImpl->mFontClient.GetPointSize(glyphToRemove.fontId));
324
325               if(!firstPenSet)
326               {
327                 const Vector2& position = *(glyphPositions + indexOfEllipsis);
328
329                 // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
330                 penY = position.y + glyphToRemove.yBearing;
331
332                 // Calculates the first penX which will be used if rtl text is elided.
333                 firstPenX = position.x - glyphToRemove.xBearing;
334                 if(firstPenX < -ellipsisGlyph.xBearing)
335                 {
336                   // Avoids to exceed the bounding box when rtl text is elided.
337                   firstPenX = -ellipsisGlyph.xBearing;
338                 }
339
340                 removedGlypsWidth = -ellipsisGlyph.xBearing;
341
342                 firstPenSet = true;
343               }
344
345               removedGlypsWidth += std::min(glyphToRemove.advance, (glyphToRemove.xBearing + glyphToRemove.width));
346
347               // Calculate the width of the ellipsis glyph and check if it fits.
348               const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
349               if((ellipsisGlyphWidth < removedGlypsWidth) || (isTailMode ? (indexOfEllipsis == 0u) : (indexOfEllipsis == numberOfGlyphs - 1u)))
350               {
351                 GlyphInfo& glyphInfo = *(glyphs + indexOfEllipsis);
352                 Vector2&   position  = *(glyphPositions + indexOfEllipsis);
353                 position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f;
354
355                 // Replace the glyph by the ellipsis glyph.
356                 glyphInfo = ellipsisGlyph;
357
358                 // Change the 'x' and 'y' position of the ellipsis glyph.
359
360                 if(position.x > firstPenX)
361                 {
362                   position.x = firstPenX + removedGlypsWidth - ellipsisGlyphWidth;
363                 }
364
365                 position.x += ellipsisGlyph.xBearing;
366                 position.y = penY - ellipsisGlyph.yBearing;
367
368                 inserted = true;
369               }
370             }
371
372             if(!inserted)
373             {
374               if(isTailMode && indexOfEllipsis > 0u)
375               {
376                 // Tail Mode: remove glyphs from startIndexOfEllipsis then decrement indexOfEllipsis, until arrive to index zero.
377                 --indexOfEllipsis;
378               }
379               else if(!isTailMode && indexOfEllipsis < numberOfLaidOutGlyphs - 1u)
380               {
381                 // Not Tail Mode: remove glyphs from startIndexOfEllipsis then increase indexOfEllipsis, until arrive to last index (numberOfGlyphs - 1u).
382                 ++indexOfEllipsis;
383               }
384               else
385               {
386                 // No space for the ellipsis.
387                 inserted = true;
388               }
389               ++numberOfRemovedGlyphs;
390             }
391           }
392
393           // 'Removes' all the glyphs after the ellipsis glyph.
394           if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
395           {
396             //Reduce size, shift glyphs and start from ellipsis glyph
397             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
398
399             GlyphIndex firstMiddleIndexOfElidedGlyphs  = 0u;
400             GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
401
402             bool isOnlySecondHalf = false;
403             if(isTailMode)
404             {
405               // Multi-lines case with MIDDLE
406               // In case the Ellipsis in the end of line,
407               // then this index will be the firstMiddleIndex.
408               // The secondMiddleIndex will be the fisrt index in next line.
409               // But in case there is no line after Ellipsis's line then secondMiddleIndex and endIndex equal firstMiddle
410               // Example:
411               // A: are laid out glyphs in line has Ellipsis in the end.
412               // N: are laid out glyphs in lines after removed lines.
413               // R: are removed glyphs.
414               // L: are removed glyphs when removed lines.
415               // AAAAAAAAAAAA...RRR    => Here's the firstMiddleIndex (First index after last A)
416               // LLLLLLLLLLLLLLL
417               // LLLLLLLLLLLLLLL
418               // NNNNNNNNNNNNNN        => Here's the secondMiddleIndex (First N)
419               // NNNNNNNNNN
420
421               firstMiddleIndexOfElidedGlyphs = indexOfEllipsis;
422               if(ellipsisNextLine != nullptr)
423               {
424                 secondMiddleIndexOfElidedGlyphs = ellipsisNextLine->glyphRun.glyphIndex;
425               }
426               else
427               {
428                 secondMiddleIndexOfElidedGlyphs = firstMiddleIndexOfElidedGlyphs;
429                 mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(firstMiddleIndexOfElidedGlyphs);
430               }
431             }
432             else
433             {
434               // Single line case with MIDDLE
435               // In case the Ellipsis in the middle of line,
436               // Then the last index in first half will be firstMiddleIndex.
437               // And the indexOfEllipsis will be secondMiddleIndex, which is the first index in second half.
438               // Example:
439               // A: are laid out glyphs in first half of line.
440               // N: are laid out glyphs in second half of line.
441               // R: are removed glyphs.
442               // L: re removed glyphs when layouting text
443               // AAAAAAALLLLLLLLLLLRRR...NNNNN
444               // firstMiddleIndex (index of last A)
445               // secondMiddleIndex (index before first N)
446
447               firstMiddleIndexOfElidedGlyphs  = (ellipsisLine->glyphRun.numberOfGlyphs > 0u) ? (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u) : (ellipsisLine->glyphRun.glyphIndex);
448               secondMiddleIndexOfElidedGlyphs = indexOfEllipsis;
449               isOnlySecondHalf                = ellipsisLine->glyphRun.numberOfGlyphs == 0u && ellipsisLine->glyphRunSecondHalf.numberOfGlyphs > 0u;
450             }
451
452             mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(firstMiddleIndexOfElidedGlyphs);
453             mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(secondMiddleIndexOfElidedGlyphs);
454
455             // The number of shifted glyphs and shifting positions will be different according to Single-line or Multi-lines.
456             // isOnlySecondHalf will be true when MIDDLE Ellipsis glyph in single line.
457             if(isOnlySecondHalf)
458             {
459               Length numberOfSecondHalfGlyphs = numberOfLaidOutGlyphs - firstMiddleIndexOfElidedGlyphs;
460
461               //Copy elided glyphs after the ellipsis glyph.
462               memcpy(glyphs + firstMiddleIndexOfElidedGlyphs, glyphs + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo));
463               memcpy(glyphPositions + firstMiddleIndexOfElidedGlyphs, glyphPositions + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2));
464             }
465             else
466             {
467               Length numberOfSecondHalfGlyphs = numberOfLaidOutGlyphs - firstMiddleIndexOfElidedGlyphs + 1u;
468
469               //Copy elided glyphs after the ellipsis glyph.
470               memcpy(glyphs + firstMiddleIndexOfElidedGlyphs + 1u, glyphs + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo));
471               memcpy(glyphPositions + firstMiddleIndexOfElidedGlyphs + 1u, glyphPositions + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2));
472             }
473           }
474           else if(ellipsisPosition == DevelText::EllipsisPosition::START)
475           {
476             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
477             //Copy elided glyphs after the ellipsis glyph.
478             memcpy(glyphs, glyphs + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(GlyphInfo));
479             memcpy(glyphPositions, glyphPositions + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(Vector2));
480             mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(indexOfEllipsis);
481           }
482           else // DevelText::EllipsisPosition::END
483           {
484             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
485             mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(indexOfEllipsis);
486           }
487         }
488       }
489     }
490   }
491
492   return numberOfLaidOutGlyphs;
493 }
494
495 const Vector4* const View::GetColors() const
496 {
497   if(mImpl->mVisualModel)
498   {
499     return mImpl->mVisualModel->mColors.Begin();
500   }
501
502   return NULL;
503 }
504
505 const ColorIndex* const View::GetColorIndices() const
506 {
507   if(mImpl->mVisualModel)
508   {
509     return mImpl->mVisualModel->mColorIndices.Begin();
510   }
511
512   return NULL;
513 }
514
515 const Vector4* const View::GetBackgroundColors() const
516 {
517   if(mImpl->mVisualModel)
518   {
519     return mImpl->mVisualModel->mBackgroundColors.Begin();
520   }
521
522   return nullptr;
523 }
524
525 const ColorIndex* const View::GetBackgroundColorIndices() const
526 {
527   if(mImpl->mVisualModel)
528   {
529     return mImpl->mVisualModel->mBackgroundColorIndices.Begin();
530   }
531
532   return nullptr;
533 }
534
535 bool const View::IsMarkupBackgroundColorSet() const
536 {
537   if(mImpl->mVisualModel)
538   {
539     return (mImpl->mVisualModel->mBackgroundColors.Count() > 0);
540   }
541
542   return false;
543 }
544
545 const Vector4& View::GetTextColor() const
546 {
547   if(mImpl->mVisualModel)
548   {
549     return mImpl->mVisualModel->GetTextColor();
550   }
551   return Vector4::ZERO;
552 }
553
554 const Vector2& View::GetShadowOffset() const
555 {
556   if(mImpl->mVisualModel)
557   {
558     return mImpl->mVisualModel->GetShadowOffset();
559   }
560   return Vector2::ZERO;
561 }
562
563 const Vector4& View::GetShadowColor() const
564 {
565   if(mImpl->mVisualModel)
566   {
567     return mImpl->mVisualModel->GetShadowColor();
568   }
569   return Vector4::ZERO;
570 }
571
572 const Vector4& View::GetUnderlineColor() const
573 {
574   if(mImpl->mVisualModel)
575   {
576     return mImpl->mVisualModel->GetUnderlineColor();
577   }
578   return Vector4::ZERO;
579 }
580
581 bool View::IsUnderlineEnabled() const
582 {
583   if(mImpl->mVisualModel)
584   {
585     return mImpl->mVisualModel->IsUnderlineEnabled();
586   }
587   return false;
588 }
589
590 const GlyphInfo* View::GetHyphens() const
591 {
592   if(mImpl->mVisualModel)
593   {
594     return mImpl->mVisualModel->mHyphen.glyph.Begin();
595   }
596
597   return nullptr;
598 }
599
600 const Length* View::GetHyphenIndices() const
601 {
602   if(mImpl->mVisualModel)
603   {
604     return mImpl->mVisualModel->mHyphen.index.Begin();
605   }
606
607   return nullptr;
608 }
609
610 Length View::GetHyphensCount() const
611 {
612   if(mImpl->mVisualModel)
613   {
614     return mImpl->mVisualModel->mHyphen.glyph.Size();
615   }
616
617   return 0;
618 }
619 float View::GetUnderlineHeight() const
620 {
621   if(mImpl->mVisualModel)
622   {
623     return mImpl->mVisualModel->GetUnderlineHeight();
624   }
625   return 0.0f;
626 }
627
628 Length View::GetNumberOfUnderlineRuns() const
629 {
630   if(mImpl->mVisualModel)
631   {
632     return mImpl->mVisualModel->GetNumberOfUnderlineRuns();
633   }
634
635   return 0u;
636 }
637
638 void View::GetUnderlineRuns(GlyphRun*         underlineRuns,
639                             UnderlineRunIndex index,
640                             Length            numberOfRuns) const
641 {
642   if(mImpl->mVisualModel)
643   {
644     mImpl->mVisualModel->GetUnderlineRuns(underlineRuns,
645                                           index,
646                                           numberOfRuns);
647   }
648 }
649
650 const Vector4& View::GetOutlineColor() const
651 {
652   if(mImpl->mVisualModel)
653   {
654     return mImpl->mVisualModel->GetOutlineColor();
655   }
656   return Vector4::ZERO;
657 }
658
659 uint16_t View::GetOutlineWidth() const
660 {
661   if(mImpl->mVisualModel)
662   {
663     return mImpl->mVisualModel->GetOutlineWidth();
664   }
665   return 0u;
666 }
667
668 DevelText::EllipsisPosition::Type View::GetEllipsisPosition() const
669 {
670   DevelText::EllipsisPosition::Type ellipsisPosition = DevelText::EllipsisPosition::END;
671   if(mImpl->mVisualModel)
672   {
673     const VisualModel& model = *mImpl->mVisualModel;
674     ellipsisPosition         = model.GetEllipsisPosition();
675   }
676
677   return ellipsisPosition;
678 }
679
680 bool View::IsTextElideEnabled() const
681 {
682   bool isTextElideEnabled = false;
683
684   if(mImpl->mVisualModel)
685   {
686     const VisualModel& model = *mImpl->mVisualModel;
687     isTextElideEnabled       = model.IsTextElideEnabled();
688   }
689
690   return isTextElideEnabled;
691 }
692
693 GlyphIndex View::GetStartIndexOfElidedGlyphs() const
694 {
695   GlyphIndex startIndexOfElidedGlyphs = 0u;
696
697   if(mImpl->mVisualModel)
698   {
699     const VisualModel& model = *mImpl->mVisualModel;
700     startIndexOfElidedGlyphs = model.GetStartIndexOfElidedGlyphs();
701   }
702
703   return startIndexOfElidedGlyphs;
704 }
705
706 GlyphIndex View::GetEndIndexOfElidedGlyphs() const
707 {
708   GlyphIndex endIndexOfElidedGlyphs = 0u;
709
710   if(mImpl->mVisualModel)
711   {
712     const VisualModel& model = *mImpl->mVisualModel;
713     endIndexOfElidedGlyphs   = model.GetEndIndexOfElidedGlyphs();
714   }
715
716   return endIndexOfElidedGlyphs;
717 }
718
719 GlyphIndex View::GetFirstMiddleIndexOfElidedGlyphs() const
720 {
721   GlyphIndex firstMiddleIndexOfElidedGlyphs = 0u;
722
723   if(mImpl->mVisualModel)
724   {
725     const VisualModel& model       = *mImpl->mVisualModel;
726     firstMiddleIndexOfElidedGlyphs = model.GetFirstMiddleIndexOfElidedGlyphs();
727   }
728
729   return firstMiddleIndexOfElidedGlyphs;
730 }
731
732 GlyphIndex View::GetSecondMiddleIndexOfElidedGlyphs() const
733 {
734   GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
735
736   if(mImpl->mVisualModel)
737   {
738     const VisualModel& model        = *mImpl->mVisualModel;
739     secondMiddleIndexOfElidedGlyphs = model.GetSecondMiddleIndexOfElidedGlyphs();
740   }
741
742   return secondMiddleIndexOfElidedGlyphs;
743 }
744
745 } // namespace Text
746
747 } // namespace Toolkit
748
749 } // namespace Dali