2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/text-geometry.h>
22 #include <dali/integration-api/debug.h>
25 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
35 bool GetNextLine(GlyphIndex index, LineIndex& lineIndex, LineRun*& lineRun, GlyphIndex& lastGlyphOfLine, Length numberOfLines)
37 if(index == lastGlyphOfLine)
40 if(lineIndex < numberOfLines)
50 void UpdateLineInfo(const LineRun* lineRun, float& currentLineOffset, float& currentLineHeight, GlyphIndex& lastGlyphOfLine)
52 lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
53 currentLineOffset = currentLineOffset + currentLineHeight;
54 currentLineHeight = GetLineHeight(*lineRun);
57 void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterIndex endIndex, Vector<Vector2>& sizesList, Vector<Vector2>& positionsList)
59 VisualModelPtr& visualModel = textModel->mVisualModel;
60 LogicalModelPtr& logicalModel = textModel->mLogicalModel;
62 const GlyphIndex* const charactersToGlyphBuffer = visualModel->mCharactersToGlyph.Begin();
63 const Length* const glyphsPerCharacterBuffer = visualModel->mGlyphsPerCharacter.Begin();
64 const GlyphInfo* const glyphsBuffer = visualModel->mGlyphs.Begin();
65 const Vector2* const positionsBuffer = visualModel->mGlyphPositions.Begin();
66 const Length* const charactersPerGlyphBuffer = visualModel->mCharactersPerGlyph.Begin();
67 const CharacterIndex* const glyphToCharacterBuffer = visualModel->mGlyphsToCharacters.Begin();
68 const CharacterDirection* const modelCharacterDirectionsBuffer = (0u != logicalModel->mCharacterDirections.Count()) ? logicalModel->mCharacterDirections.Begin() : NULL;
70 if((startIndex < 0 && endIndex < 0) || (startIndex >= logicalModel->mText.Count() && endIndex >= logicalModel->mText.Count()))
79 if(startIndex >= logicalModel->mText.Count())
80 startIndex = logicalModel->mText.Count() - 1;
82 if(endIndex >= logicalModel->mText.Count())
83 endIndex = logicalModel->mText.Count() - 1;
85 if(startIndex > endIndex)
87 std::swap(startIndex, endIndex);
90 LineRun* lineRun = visualModel->mLines.Begin();
91 GlyphIndex glyphStart = *(charactersToGlyphBuffer + startIndex);
93 //if glyph not in the first line (in some ellipsis cases)
94 if(glyphStart < lineRun->glyphRun.glyphIndex)
96 glyphStart = lineRun->glyphRun.glyphIndex;
97 startIndex = *(glyphToCharacterBuffer + glyphStart);
99 if(startIndex > endIndex)
101 std::swap(startIndex, endIndex);
105 const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + endIndex);
106 GlyphIndex glyphEnd = *(charactersToGlyphBuffer + endIndex) + ((numberOfGlyphs > 0) ? numberOfGlyphs - 1u : 0u);
107 LineIndex lineIndex = visualModel->GetLineOfCharacter(startIndex);
108 Length numberOfLines = visualModel->GetTotalNumberOfLines();
110 LineIndex firstLineIndex = lineIndex;
112 Vector2 textInLinePosition;
114 lineRun += firstLineIndex;
116 //get the first line and its vertical offset
117 float currentLineOffset = CalculateLineOffset(visualModel->mLines, firstLineIndex);
118 float currentLineHeight = GetLineHeight(*lineRun);
119 GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1;
121 // Check if the first/last glyph is a ligature that needs be splitted like English fi or Arabic ﻻ.
122 const Length numberOfCharactersStart = *(charactersPerGlyphBuffer + glyphStart);
123 const Length numberOfCharactersEnd = *(charactersPerGlyphBuffer + glyphEnd);
125 bool splitStartGlyph = (numberOfCharactersStart > 1u) && HasLigatureMustBreak(logicalModel->GetScript(startIndex));
126 bool splitEndGlyph = (glyphStart != glyphEnd) && (numberOfCharactersEnd > 1u) && HasLigatureMustBreak(logicalModel->GetScript(endIndex));
129 Vector2 currentPosition;
132 CharacterDirection isCurrentRightToLeft;
134 CharacterDirection isPrevoiusRightToLeft = (nullptr != modelCharacterDirectionsBuffer ? *(modelCharacterDirectionsBuffer + startIndex) : false);
135 const bool isEllipsisEnabled = textModel->mElideEnabled;
136 const GlyphIndex startIndexOfGlyphs = textModel->GetStartIndexOfElidedGlyphs();
137 const GlyphIndex endIndexOfGlyphs = textModel->GetEndIndexOfElidedGlyphs();
138 const GlyphIndex firstMiddleIndexOfElidedGlyphs = textModel->GetFirstMiddleIndexOfElidedGlyphs();
139 const GlyphIndex secondMiddleIndexOfElidedGlyphs = textModel->GetSecondMiddleIndexOfElidedGlyphs();
140 const DevelText::EllipsisPosition::Type ellipsisPosition = textModel->GetEllipsisPosition();
142 for(GlyphIndex index = glyphStart; index <= glyphEnd; ++index)
144 if(isEllipsisEnabled)
146 if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
148 if(index >= firstMiddleIndexOfElidedGlyphs &&
149 index < secondMiddleIndexOfElidedGlyphs)
151 if((index - 1 == firstMiddleIndexOfElidedGlyphs) && (firstMiddleIndexOfElidedGlyphs != 0))
153 sizesList.PushBack(blockSize);
154 positionsList.PushBack(blockPos);
157 if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
159 UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
161 // Ignore any glyph that was removed
167 if((ellipsisPosition == DevelText::EllipsisPosition::END) && (index >= endIndexOfGlyphs))
169 //skip remaining elided glyphs
172 else if((ellipsisPosition == DevelText::EllipsisPosition::START) && (index <= startIndexOfGlyphs))
174 if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
176 UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
184 const GlyphInfo& glyph = *(glyphsBuffer + index);
185 const Vector2& position = *(positionsBuffer + index);
187 // If NULL, means all of the characters is left to right.
188 isCurrentRightToLeft = (nullptr != modelCharacterDirectionsBuffer ? *(modelCharacterDirectionsBuffer + *(glyphToCharacterBuffer + index)) : false);
190 if(splitStartGlyph && (index == glyphStart))
192 // If the first glyph is a ligature that needs to be splitted, we may need only to add part of the glyph.
193 const float glyphAdvance = glyph.advance / static_cast<float>(numberOfCharactersStart);
194 const CharacterIndex interGlyphIndex = startIndex - *(glyphToCharacterBuffer + glyphStart);
195 const Length numberOfCharacters = (glyphStart == glyphEnd) ? (endIndex - startIndex) + 1 : (numberOfCharactersStart - interGlyphIndex);
197 currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x + glyphAdvance * static_cast<float>(isCurrentRightToLeft ? (numberOfCharactersStart - interGlyphIndex - numberOfCharacters) : interGlyphIndex);
198 currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
199 currentSize.x = static_cast<float>(numberOfCharacters) * glyphAdvance;
200 currentSize.y = currentLineHeight;
201 splitStartGlyph = false;
203 else if(splitEndGlyph && (index == glyphEnd))
205 const float glyphAdvance = glyph.advance / static_cast<float>(numberOfCharactersEnd);
206 const CharacterIndex interGlyphIndex = endIndex - *(glyphToCharacterBuffer + glyphEnd);
207 const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex - 1;
209 currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x + (isCurrentRightToLeft ? (glyphAdvance * static_cast<float>(numberOfCharacters)) : 0.f);
210 currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
211 currentSize.x = static_cast<float>(interGlyphIndex + 1) * glyphAdvance;
212 currentSize.y = currentLineHeight;
213 splitEndGlyph = false;
217 currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x;
218 currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
219 currentSize.x = glyph.advance;
220 currentSize.y = currentLineHeight;
222 // if there is next line to retrieve.
223 if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
225 UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
229 if((index == glyphStart) || (isEllipsisEnabled && (((ellipsisPosition == DevelText::EllipsisPosition::MIDDLE) && (index == secondMiddleIndexOfElidedGlyphs)) || ((ellipsisPosition == DevelText::EllipsisPosition::START) && (index - 1 == startIndexOfGlyphs)))))
231 blockPos = currentPosition;
232 blockSize = currentSize;
234 else if((isPrevoiusRightToLeft != isCurrentRightToLeft) || (blockPos.y != currentPosition.y)) //new direction or new line
236 sizesList.PushBack(blockSize);
237 positionsList.PushBack(blockPos);
239 blockPos = currentPosition;
240 blockSize = currentSize;
244 if(isCurrentRightToLeft)
246 blockPos.x -= currentSize.x;
249 blockSize.x += currentSize.x;
252 isPrevoiusRightToLeft = isCurrentRightToLeft;
256 sizesList.PushBack(blockSize);
257 positionsList.PushBack(blockPos);
262 } // namespace Toolkit