2 * Copyright (c) 2022 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/layouts/layout-engine.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-engine-helper-functions.h>
32 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
40 float GetLineHeight(const LineRun lineRun)
42 // The line height is the addition of the line ascender, the line descender and the line spacing.
43 // However, the line descender has a negative value, hence the subtraction.
44 return lineRun.ascender - lineRun.descender + lineRun.lineSpacing;
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const CharacterDirection LTR = false;
57 const CharacterDirection RTL = !LTR;
58 const float LINE_SPACING = 0.f;
59 const float MIN_LINE_SIZE = 0.f;
60 const Character HYPHEN_UNICODE = 0x002D;
62 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
64 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
70 * @brief Stores temporary layout info of the line.
78 numberOfCharacters{0u},
85 whiteSpaceLengthEndOfLine{0.f},
87 isSplitToTwoHalves(false),
88 glyphIndexInSecondHalfLine{0u},
89 characterIndexInSecondHalfLine{0u},
90 numberOfGlyphsInSecondHalfLine{0u},
91 numberOfCharactersInSecondHalfLine{0u}
105 numberOfCharacters = 0u;
106 ascender = -MAX_FLOAT;
107 descender = MAX_FLOAT;
109 isSplitToTwoHalves = false;
110 glyphIndexInSecondHalfLine = 0u;
111 characterIndexInSecondHalfLine = 0u;
112 numberOfGlyphsInSecondHalfLine = 0u;
113 numberOfCharactersInSecondHalfLine = 0u;
116 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
117 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
118 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
119 Length numberOfCharacters; ///< The number of characters which fit in one line.
120 float ascender; ///< The maximum ascender of all fonts in the line.
121 float descender; ///< The minimum descender of all fonts in the line.
122 float lineSpacing; ///< The line spacing
123 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
124 float previousAdvance; ///< The advance of the previous glyph.
125 float length; ///< The current length of the line.
126 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
127 CharacterDirection direction;
129 bool isSplitToTwoHalves; ///< Whether the second half is defined.
130 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
131 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
132 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
133 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
136 struct LayoutBidiParameters
140 paragraphDirection = LTR;
141 bidiParagraphIndex = 0u;
143 isBidirectional = false;
146 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
147 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
148 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
149 bool isBidirectional = false; ///< Whether the text is bidirectional.
155 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
157 mDefaultLineSpacing{LINE_SPACING},
158 mDefaultLineSize{MIN_LINE_SIZE}
163 * @brief Updates the line ascender and descender with the metrics of a new font.
165 * @param[in] glyphMetrics The metrics of the new font.
166 * @param[in,out] lineLayout The line layout.
168 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
170 Text::FontMetrics fontMetrics;
171 if(0u != glyphMetrics.fontId)
173 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
177 fontMetrics.ascender = glyphMetrics.fontHeight;
178 fontMetrics.descender = 0.f;
179 fontMetrics.height = fontMetrics.ascender;
180 fontMetrics.underlinePosition = 0.f;
181 fontMetrics.underlineThickness = 1.f;
184 // Sets the maximum ascender.
185 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
187 // Sets the minimum descender.
188 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
190 // Sets the line size
191 lineLayout.lineSpacing = mDefaultLineSize - (lineLayout.ascender + -lineLayout.descender);
192 lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
194 // Add the line spacing
195 lineLayout.lineSpacing += mDefaultLineSpacing;
199 * @brief Merges a temporary line layout into the line layout.
201 * @param[in,out] lineLayout The line layout.
202 * @param[in] tmpLineLayout A temporary line layout.
203 * @param[in] isShifted Whether to shift first glyph and character indices.
205 void MergeLineLayout(LineLayout& lineLayout,
206 const LineLayout& tmpLineLayout,
209 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
210 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
212 lineLayout.penX = tmpLineLayout.penX;
213 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
215 lineLayout.length = tmpLineLayout.length;
216 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
218 // Sets the maximum ascender.
219 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
221 // Sets the minimum descender.
222 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
224 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
227 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
228 lineLayout.characterIndex = tmpLineLayout.characterIndex;
231 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
232 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
233 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
234 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
235 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
238 void LayoutRightToLeft(const Parameters& parameters,
239 const BidirectionalLineInfoRun& bidirectionalLineInfo,
241 float& whiteSpaceLengthEndOfLine)
243 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
244 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
245 // then the first half of line "characterRun",
246 // Otherwise travers whole characters in"characterRun".
248 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
249 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
250 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
251 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
253 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
254 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
255 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
257 CharacterIndex characterLogicalIndex = 0u;
258 CharacterIndex characterVisualIndex = 0u;
260 float calculatedAdvance = 0.f;
262 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
263 // Otherwise maps the first visual index from visualToLogicalMap.
264 // This is to initialize the first visual index.
265 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
267 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
271 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
274 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
276 if(RTL == bidirectionalLineInfo.direction)
278 // If there are characters in the second half of Line.
279 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
281 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
282 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
284 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
286 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
287 whiteSpaceLengthEndOfLine += calculatedAdvance;
289 ++characterLogicalIndex;
290 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
294 // If all characters in the second half of Line are WhiteSpaces.
295 // then continue adding the WhiteSpaces from the first hel of Line.
296 // Also this is valid when the line was not splitted.
297 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
299 extendedToSecondHalf = true; // Whether the logical index is extended to second half
300 characterLogicalIndex = 0u;
301 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
303 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
304 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
306 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
308 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
309 whiteSpaceLengthEndOfLine += calculatedAdvance;
311 ++characterLogicalIndex;
312 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
317 // Here's the first index of character is not WhiteSpace
318 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
320 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
321 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
322 lastGlyphOfParagraphPlusOne,
323 charactersPerGlyphBuffer);
325 GlyphMetrics glyphMetrics;
326 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
327 GetGlyphsMetrics(glyphIndex,
328 numberOfGLyphsInGroup,
334 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
336 // Traverses the characters of the right to left paragraph.
337 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
338 if(!extendedToSecondHalf &&
339 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
341 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
343 // Convert the character in the logical order into the character in the visual order.
344 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
345 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
347 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
349 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
350 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
351 lastGlyphOfParagraphPlusOne,
352 charactersPerGlyphBuffer);
354 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
356 GlyphMetrics glyphMetrics;
357 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
358 GetGlyphsMetrics(glyphIndex,
359 numberOfGLyphsInGroup,
367 // If glyph is WhiteSpace then:
368 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
369 // the endOfLine in RTL was the headOfLine for layouting.
370 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
371 if(RTL == bidirectionalLineInfo.direction)
373 length += glyphMetrics.advance;
377 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
379 penX += glyphMetrics.advance;
383 // If glyph is not whiteSpace then:
384 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
385 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
386 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
387 // Otherwise the current length is maximum.
388 if(LTR == bidirectionalLineInfo.direction)
390 whiteSpaceLengthEndOfLine = 0.f;
392 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
393 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
398 // Continue traversing in the first half of line or in the whole line.
399 // If the second half of line was extended then continue from logical index in the first half of line
400 // Also this is valid when the line was not splitted and there were WhiteSpace.
401 // Otherwise start from first logical index in line.
402 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
403 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
405 // Convert the character in the logical order into the character in the visual order.
406 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
407 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
409 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
411 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
412 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
413 lastGlyphOfParagraphPlusOne,
414 charactersPerGlyphBuffer);
416 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
418 GlyphMetrics glyphMetrics;
419 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
420 GetGlyphsMetrics(glyphIndex,
421 numberOfGLyphsInGroup,
429 // If glyph is WhiteSpace then:
430 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
431 // the endOfLine in RTL was the headOfLine for layouting.
432 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
433 if(RTL == bidirectionalLineInfo.direction)
435 length += glyphMetrics.advance;
439 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
441 penX += glyphMetrics.advance;
445 // If glyph is not whiteSpace then:
446 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
447 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
448 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
449 // Otherwise the current length is maximum.
450 if(LTR == bidirectionalLineInfo.direction)
452 whiteSpaceLengthEndOfLine = 0.f;
454 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
455 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
460 void ReorderBiDiLayout(const Parameters& parameters,
461 LayoutBidiParameters& bidiParameters,
462 const LineLayout& currentLineLayout,
463 LineLayout& lineLayout,
464 bool breakInCharacters,
465 bool enforceEllipsisInSingleLine)
467 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
469 // The last glyph to be laid-out.
470 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
472 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
474 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
475 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
476 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
478 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
480 // Sets the visual to logical map tables needed to reorder the text.
481 ReorderLine(bidirectionalParagraphInfo,
482 bidirectionalLinesInfo,
483 bidiParameters.bidiLineIndex,
484 lineLayout.characterIndex,
485 lineLayout.numberOfCharacters,
486 lineLayout.characterIndexInSecondHalfLine,
487 lineLayout.numberOfCharactersInSecondHalfLine,
488 bidiParameters.paragraphDirection);
490 // Recalculate the length of the line and update the layout.
491 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
493 if(!bidirectionalLineInfo.isIdentity)
496 float whiteSpaceLengthEndOfLine = 0.f;
497 LayoutRightToLeft(parameters,
498 bidirectionalLineInfo,
500 whiteSpaceLengthEndOfLine);
502 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
503 if(!Equals(length, lineLayout.length))
505 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
507 if(isMultiline && (length > parameters.boundingBox.width))
509 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
511 // The word doesn't fit in one line. It has to be split by character.
513 // Remove the last laid out glyph(s) as they doesn't fit.
514 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
516 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
517 lastGlyphOfParagraphPlusOne,
518 charactersPerGlyphBuffer);
520 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
522 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
523 lineLayout.numberOfCharacters -= numberOfCharacters;
525 AdjustLayout(parameters,
527 bidirectionalParagraphInfo,
530 if(lineLayout.length < parameters.boundingBox.width)
535 if(glyphIndex < numberOfGLyphsInGroup)
537 // avoids go under zero for an unsigned int.
541 glyphIndex -= numberOfGLyphsInGroup;
546 lineLayout = currentLineLayout;
548 AdjustLayout(parameters,
550 bidirectionalParagraphInfo,
556 lineLayout.length = std::max(length, lineLayout.length);
563 void AdjustLayout(const Parameters& parameters,
564 LayoutBidiParameters& bidiParameters,
565 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
566 LineLayout& lineLayout)
568 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
570 // Remove current reordered line.
571 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
573 // Re-build the conversion table without the removed glyphs.
574 ReorderLine(bidirectionalParagraphInfo,
575 bidirectionalLinesInfo,
576 bidiParameters.bidiLineIndex,
577 lineLayout.characterIndex,
578 lineLayout.numberOfCharacters,
579 lineLayout.characterIndexInSecondHalfLine,
580 lineLayout.numberOfCharactersInSecondHalfLine,
581 bidiParameters.paragraphDirection);
583 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
586 float whiteSpaceLengthEndOfLine = 0.f;
587 LayoutRightToLeft(parameters,
588 bidirectionalLineInfo,
590 whiteSpaceLengthEndOfLine);
592 lineLayout.length = length;
593 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
597 * Retrieves the line layout for a given box width.
599 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
600 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
601 * to reorder the line and recalculate its length.
604 * @param[in] parameters The layout parameters.
605 * @param[] bidiParameters Bidirectional info for the current line.
606 * @param[out] lineLayout The line layout.
607 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
608 * @param[in] ellipsisPosition Where is the location the text elide
610 void GetLineLayoutForBox(const Parameters& parameters,
611 LayoutBidiParameters& bidiParameters,
612 LineLayout& lineLayout,
614 DevelText::EllipsisPosition::Type ellipsisPosition,
615 bool enforceEllipsisInSingleLine,
616 bool elideTextEnabled)
618 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
619 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
621 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
622 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
623 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
624 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
625 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
627 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
628 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
630 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
631 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
632 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
633 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
634 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
635 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
637 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
639 // The last glyph to be laid-out.
640 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
642 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
643 // In the case the line starts with a right to left character, if the width is longer than the advance,
644 // the difference needs to be added to the line length.
646 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
647 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
648 lastGlyphOfParagraphPlusOne,
649 charactersPerGlyphBuffer);
651 float targetWidth = parameters.boundingBox.width;
652 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
654 bool isSecondHalf = false;
656 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
657 float calculatedAdvance = 0.f;
658 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
659 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
661 GlyphMetrics glyphMetrics;
662 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
663 GetGlyphsMetrics(lineLayout.glyphIndex,
664 numberOfGLyphsInGroup,
670 // Set the direction of the first character of the line.
671 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
673 // Stores temporary line layout which has not been added to the final line layout.
674 LineLayout tmpLineLayout;
676 // Initialize the start point.
678 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
679 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
680 // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined.
681 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
683 // Calculate the line height if there is no characters.
684 FontId lastFontId = glyphMetrics.fontId;
685 UpdateLineHeight(glyphMetrics, tmpLineLayout);
687 bool oneWordLaidOut = false;
688 bool oneHyphenLaidOut = false;
689 GlyphIndex hyphenIndex = 0;
690 GlyphInfo hyphenGlyph;
692 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
693 glyphIndex < lastGlyphOfParagraphPlusOne;)
695 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
697 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
698 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
699 lastGlyphOfParagraphPlusOne,
700 charactersPerGlyphBuffer);
702 GlyphMetrics glyphMetrics;
703 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
704 GetGlyphsMetrics(glyphIndex,
705 numberOfGLyphsInGroup,
711 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
713 // Check if the font of the current glyph is the same of the previous one.
714 // If it's different the ascender and descender need to be updated.
715 if(lastFontId != glyphMetrics.fontId)
717 UpdateLineHeight(glyphMetrics, tmpLineLayout);
718 lastFontId = glyphMetrics.fontId;
721 // Get the character indices for the current glyph. The last character index is needed
722 // because there are glyphs formed by more than one character but their break info is
723 // given only for the last character.
724 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
725 const bool hasCharacters = charactersPerGlyph > 0u;
726 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
727 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
729 // Get the line break info for the current character.
730 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
734 // Increase the number of characters.
735 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
737 // Increase the number of glyphs.
738 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
742 // Increase the number of characters.
743 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
745 // Increase the number of glyphs.
746 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
749 // Check whether is a white space.
750 const Character character = *(textBuffer + characterFirstIndex);
751 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
753 // Calculate the length of the line.
755 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
756 const float previousTmpPenX = tmpLineLayout.penX;
757 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
758 const float previousTmpLength = tmpLineLayout.length;
759 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
763 // Add the length to the length of white spaces at the end of the line.
764 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
765 // The advance is used as the width is always zero for the white spaces.
769 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
770 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
772 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
774 // Clear the white space length at the end of the line.
775 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
778 if(isSplitToTwoHalves && (!isSecondHalf) &&
779 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
781 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
782 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
784 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
785 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
787 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
788 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
790 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
792 // Check if the accumulated length fits in the width of the box.
793 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
794 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
795 completelyFill && !isMultiline &&
796 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
798 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
800 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
802 GlyphMetrics glyphMetrics;
803 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
804 GetGlyphsMetrics(glyphIndexToRemove,
805 numberOfGLyphsInGroup,
811 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
812 lastGlyphOfParagraphPlusOne,
813 charactersPerGlyphBuffer);
815 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
816 const bool hasCharacters = charactersPerGlyph > 0u;
817 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
818 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
820 // Check whether is a white space.
821 const Character character = *(textBuffer + characterFirstIndex);
822 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
826 // Decrease the number of characters for SecondHalf.
827 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
829 // Decrease the number of glyphs for SecondHalf.
830 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
834 // Decrease the number of characters.
835 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
837 // Decrease the number of glyphs.
838 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
841 if(isRemovedGlyphWhiteSpace)
843 tmpLineLayout.penX -= glyphMetrics.advance;
844 tmpLineLayout.length -= glyphMetrics.advance;
848 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
849 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
854 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
855 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
856 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
860 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
861 tmpLineLayout.characterIndex = characterLastIndex + 1u;
862 glyphIndexToRemove = tmpLineLayout.glyphIndex;
866 else if((completelyFill || isMultiline) &&
867 (tmpLineLayout.length > targetWidth))
869 // Current word does not fit in the box's width.
870 if(((oneHyphenLaidOut && isHyphenMode) ||
871 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
874 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
875 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
878 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
880 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
882 // The word doesn't fit in the control's width. It needs to be split by character.
883 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
887 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
888 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
892 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
893 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
896 tmpLineLayout.penX = previousTmpPenX;
897 tmpLineLayout.previousAdvance = previousTmpAdvance;
898 tmpLineLayout.length = previousTmpLength;
899 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
902 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
904 // Add part of the word to the line layout and shift the first glyph.
905 MergeLineLayout(lineLayout, tmpLineLayout, true);
907 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
908 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
910 // Add part of the word to the line layout.
911 MergeLineLayout(lineLayout, tmpLineLayout, false);
916 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
919 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
921 // Reorder the RTL line.
922 if(bidiParameters.isBidirectional)
924 ReorderBiDiLayout(parameters,
929 enforceEllipsisInSingleLine);
935 if((isMultiline || isLastGlyph) &&
936 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
938 LineLayout currentLineLayout = lineLayout;
939 oneHyphenLaidOut = false;
941 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
943 // Must break the line. Update the line layout, shift the first glyph and return.
944 MergeLineLayout(lineLayout, tmpLineLayout, true);
948 // Must break the line. Update the line layout and return.
949 MergeLineLayout(lineLayout, tmpLineLayout, false);
952 // Reorder the RTL line.
953 if(bidiParameters.isBidirectional)
955 ReorderBiDiLayout(parameters,
960 enforceEllipsisInSingleLine);
963 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
964 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
970 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
972 oneHyphenLaidOut = false;
973 oneWordLaidOut = isWordLaidOut;
974 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
976 // Current glyph is the last one of the current word.
977 // Add the temporal layout to the current one.
978 MergeLineLayout(lineLayout, tmpLineLayout, false);
980 tmpLineLayout.Clear();
984 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
985 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
987 hyphenGlyph = GlyphInfo();
988 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
990 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
991 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
993 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
995 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
997 hyphenIndex = glyphIndex;
998 oneHyphenLaidOut = true;
1000 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1002 // Current glyph is the last one of the current word hyphen.
1003 // Add the temporal layout to the current one.
1004 MergeLineLayout(lineLayout, tmpLineLayout, false);
1006 tmpLineLayout.Clear();
1010 glyphIndex += numberOfGLyphsInGroup;
1013 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1016 void SetGlyphPositions(const Parameters& layoutParameters,
1017 Vector2* glyphPositionsBuffer,
1018 const LineLayout& layout)
1020 // Traverse the glyphs and set the positions.
1022 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1023 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1024 const Length numberOfGlyphs = layout.numberOfGlyphs;
1025 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1027 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1028 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1030 // Check if the x bearing of the first character is negative.
1031 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1032 // so the penX position needs to be moved to the right.
1033 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1034 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1036 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1037 layoutParameters.textModel->mLogicalModel,
1038 interGlyphExtraAdvance,
1040 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1041 startIndexForGlyphPositions,
1042 glyphPositionsBuffer,
1045 if(layout.isSplitToTwoHalves)
1047 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1048 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1049 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1051 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1052 layoutParameters.textModel->mLogicalModel,
1053 interGlyphExtraAdvance,
1054 numberOfGlyphsInSecondHalfLine,
1055 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1056 startIndexForGlyphPositionsnSecondHalf,
1057 glyphPositionsBuffer,
1062 void SetGlyphPositions(const Parameters& layoutParameters,
1063 Vector2* glyphPositionsBuffer,
1064 LayoutBidiParameters& layoutBidiParameters,
1065 const LineLayout& layout)
1067 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1068 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1069 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1071 CharacterIndex characterLogicalIndex = 0u;
1072 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1073 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1077 if(layout.isSplitToTwoHalves)
1079 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1080 layoutParameters.textModel->mLogicalModel,
1081 layoutBidiParameters.bidiLineIndex,
1082 layoutParameters.startGlyphIndex,
1083 glyphPositionsBuffer,
1084 characterVisualIndex,
1085 characterLogicalIndex,
1089 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1091 extendedToSecondHalf = true;
1092 characterLogicalIndex = 0u;
1093 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1095 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1096 layoutParameters.textModel->mLogicalModel,
1097 layoutBidiParameters.bidiLineIndex,
1098 layoutParameters.startGlyphIndex,
1099 glyphPositionsBuffer,
1100 characterVisualIndex,
1101 characterLogicalIndex,
1105 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1106 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1108 penX += -glyph.xBearing;
1110 // Traverses the characters of the right to left paragraph.
1111 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1113 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1114 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1115 layoutParameters.startGlyphIndex,
1116 layoutParameters.interGlyphExtraAdvance,
1117 bidiLine.characterRunForSecondHalfLine,
1118 bidiLine.visualToLogicalMapSecondHalf,
1119 glyphPositionsBuffer,
1120 characterLogicalIndex,
1124 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1126 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1127 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1128 layoutParameters.startGlyphIndex,
1129 layoutParameters.interGlyphExtraAdvance,
1130 bidiLine.characterRun,
1131 bidiLine.visualToLogicalMap,
1132 glyphPositionsBuffer,
1133 characterLogicalIndex,
1138 * @brief Resizes the line buffer.
1140 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1141 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1142 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1143 * @param[in] updateCurrentBuffer Whether the layout is updated.
1145 * @return Pointer to either lines or newLines.
1147 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1148 Vector<LineRun>& newLines,
1149 Length& linesCapacity,
1150 bool updateCurrentBuffer)
1152 LineRun* linesBuffer = nullptr;
1153 // Reserve more space for the next lines.
1154 linesCapacity *= 2u;
1155 if(updateCurrentBuffer)
1157 newLines.Resize(linesCapacity);
1158 linesBuffer = newLines.Begin();
1162 lines.Resize(linesCapacity);
1163 linesBuffer = lines.Begin();
1170 * Ellipsis a line if it exceeds the width's of the bounding box.
1172 * @param[in] layoutParameters The parameters needed to layout the text.
1173 * @param[in] layout The line layout.
1174 * @param[in,out] layoutSize The text's layout size.
1175 * @param[in,out] linesBuffer Pointer to the line's buffer.
1176 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1177 * @param[in,out] numberOfLines The number of laid-out lines.
1178 * @param[in] penY The vertical layout position.
1179 * @param[in] currentParagraphDirection The current paragraph's direction.
1180 * @param[in,out] isAutoScrollEnabled If the isAutoScrollEnabled is true and the height of the text exceeds the boundaries of the control the text is elided and the isAutoScrollEnabled is set to false to disable the autoscroll
1181 * @param[in] ellipsisPosition Where is the location the text elide
1183 * return Whether the line is ellipsized.
1185 bool EllipsisLine(const Parameters& layoutParameters,
1186 LayoutBidiParameters& layoutBidiParameters,
1187 const LineLayout& layout,
1189 LineRun* linesBuffer,
1190 Vector2* glyphPositionsBuffer,
1191 Length& numberOfLines,
1193 bool& isAutoScrollEnabled,
1194 DevelText::EllipsisPosition::Type ellipsisPosition,
1195 bool enforceEllipsisInSingleLine)
1197 const bool ellipsis = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? (penY - layout.descender > layoutParameters.boundingBox.height) : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
1198 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1199 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1201 isAutoScrollEnabled = false;
1202 // Do not layout more lines if ellipsis is enabled.
1204 // The last line needs to be completely filled with characters.
1205 // Part of a word may be used.
1207 LineRun* lineRun = nullptr;
1208 LineLayout ellipsisLayout;
1209 if(0u != numberOfLines)
1211 // Get the last line and layout it again with the 'completelyFill' flag to true.
1212 lineRun = linesBuffer + (numberOfLines - 1u);
1213 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1215 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1219 // At least there is space reserved for one line.
1220 lineRun = linesBuffer;
1222 lineRun->glyphRun.glyphIndex = 0u;
1223 ellipsisLayout.glyphIndex = 0u;
1224 lineRun->isSplitToTwoHalves = false;
1229 GetLineLayoutForBox(layoutParameters,
1230 layoutBidiParameters,
1234 enforceEllipsisInSingleLine,
1237 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1239 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1242 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1243 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1244 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1245 lineRun->width = ellipsisLayout.length;
1246 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1247 lineRun->ascender = ellipsisLayout.ascender;
1248 lineRun->descender = ellipsisLayout.descender;
1249 lineRun->ellipsis = true;
1251 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1252 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1253 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1254 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1255 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1257 layoutSize.width = layoutParameters.boundingBox.width;
1258 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1260 layoutSize.height += GetLineHeight(*lineRun);
1263 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1265 if(layoutBidiParameters.isBidirectional)
1267 layoutBidiParameters.bidiLineIndex = 0u;
1268 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1269 endIt = bidirectionalLinesInfo.End();
1271 ++it, ++layoutBidiParameters.bidiLineIndex)
1273 const BidirectionalLineInfoRun& run = *it;
1274 //To handle case when the laid characters exist in next line.
1275 //More than one BidirectionalLineInfoRun could start with same character.
1276 //When need to check also numberOfCharacters in line.
1277 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1278 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1279 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1280 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1281 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1283 // Found where to insert the bidi line info.
1289 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1291 if((nullptr != bidirectionalLineInfo) &&
1292 !bidirectionalLineInfo->isIdentity &&
1293 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1295 lineRun->direction = RTL;
1296 SetGlyphPositions(layoutParameters,
1297 glyphPositionsBuffer,
1298 layoutBidiParameters,
1303 lineRun->direction = LTR;
1304 SetGlyphPositions(layoutParameters,
1305 glyphPositionsBuffer,
1314 * @brief Updates the text layout with a new laid-out line.
1316 * @param[in] layoutParameters The parameters needed to layout the text.
1317 * @param[in] layout The line layout.
1318 * @param[in,out] layoutSize The text's layout size.
1319 * @param[in,out] linesBuffer Pointer to the line's buffer.
1320 * @param[in] index Index to the vector of glyphs.
1321 * @param[in,out] numberOfLines The number of laid-out lines.
1322 * @param[in] isLastLine Whether the laid-out line is the last one.
1324 void UpdateTextLayout(const Parameters& layoutParameters,
1325 const LineLayout& layout,
1327 LineRun* linesBuffer,
1329 Length& numberOfLines,
1332 LineRun& lineRun = *(linesBuffer + numberOfLines);
1335 lineRun.glyphRun.glyphIndex = index;
1336 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1337 lineRun.characterRun.characterIndex = layout.characterIndex;
1338 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1339 lineRun.width = layout.length;
1340 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1342 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1343 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1344 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1345 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1346 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1348 // Rounds upward to avoid a non integer size.
1349 lineRun.width = std::ceil(lineRun.width);
1351 lineRun.ascender = layout.ascender;
1352 lineRun.descender = layout.descender;
1353 lineRun.direction = layout.direction;
1354 lineRun.ellipsis = false;
1356 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
1357 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1359 lineRun.lineSpacing += mDefaultLineSpacing;
1361 // Update the actual size.
1362 if(lineRun.width > layoutSize.width)
1364 layoutSize.width = lineRun.width;
1367 layoutSize.height += GetLineHeight(lineRun);
1371 * @brief Updates the text layout with the last laid-out line.
1373 * @param[in] layoutParameters The parameters needed to layout the text.
1374 * @param[in] characterIndex The character index of the line.
1375 * @param[in] glyphIndex The glyph index of the line.
1376 * @param[in,out] layoutSize The text's layout size.
1377 * @param[in,out] linesBuffer Pointer to the line's buffer.
1378 * @param[in,out] numberOfLines The number of laid-out lines.
1380 void UpdateTextLayout(const Parameters& layoutParameters,
1381 CharacterIndex characterIndex,
1382 GlyphIndex glyphIndex,
1384 LineRun* linesBuffer,
1385 Length& numberOfLines)
1387 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1389 // Need to add a new line with no characters but with height to increase the layoutSize.height
1390 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1392 Text::FontMetrics fontMetrics;
1393 if(0u != glyphInfo.fontId)
1395 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1398 LineRun& lineRun = *(linesBuffer + numberOfLines);
1401 lineRun.glyphRun.glyphIndex = glyphIndex;
1402 lineRun.glyphRun.numberOfGlyphs = 0u;
1403 lineRun.characterRun.characterIndex = characterIndex;
1404 lineRun.characterRun.numberOfCharacters = 0u;
1405 lineRun.width = 0.f;
1406 lineRun.ascender = fontMetrics.ascender;
1407 lineRun.descender = fontMetrics.descender;
1408 lineRun.extraLength = 0.f;
1409 lineRun.alignmentOffset = 0.f;
1410 lineRun.direction = LTR;
1411 lineRun.ellipsis = false;
1413 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
1414 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1416 lineRun.lineSpacing += mDefaultLineSpacing;
1418 layoutSize.height += GetLineHeight(lineRun);
1422 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1424 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1425 * @param[in,out] layoutSize The text's layout size.
1427 void UpdateLayoutSize(const Vector<LineRun>& lines,
1430 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1431 endIt = lines.End();
1435 const LineRun& line = *it;
1437 if(line.width > layoutSize.width)
1439 layoutSize.width = line.width;
1442 layoutSize.height += GetLineHeight(line);
1447 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1449 * @param[in] layoutParameters The parameters needed to layout the text.
1450 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1451 * @param[in] characterOffset The offset to be added to the runs of characters.
1452 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1454 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1455 Vector<LineRun>& lines,
1456 Length characterOffset,
1459 // Update the glyph and character runs.
1460 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1461 endIt = lines.End();
1465 LineRun& line = *it;
1467 line.glyphRun.glyphIndex = glyphOffset;
1468 line.characterRun.characterIndex = characterOffset;
1470 glyphOffset += line.glyphRun.numberOfGlyphs;
1471 characterOffset += line.characterRun.numberOfCharacters;
1475 bool LayoutText(Parameters& layoutParameters,
1477 bool elideTextEnabled,
1478 bool& isAutoScrollEnabled,
1479 DevelText::EllipsisPosition::Type ellipsisPosition)
1481 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1482 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1484 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1485 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1487 //Reset indices of ElidedGlyphs
1488 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1489 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1490 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1491 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1493 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1495 if(0u == layoutParameters.numberOfGlyphs)
1497 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1498 if(layoutParameters.isLastNewParagraph)
1500 Length numberOfLines = lines.Count();
1501 if(0u != numberOfLines)
1503 const LineRun& lastLine = *(lines.End() - 1u);
1505 if(0u != lastLine.characterRun.numberOfCharacters)
1507 // Need to add a new line with no characters but with height to increase the layoutSize.height
1509 Initialize(newLine);
1510 lines.PushBack(newLine);
1512 UpdateTextLayout(layoutParameters,
1513 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1514 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1522 // Calculates the layout size.
1523 UpdateLayoutSize(lines,
1526 // Rounds upward to avoid a non integer size.
1527 layoutSize.height = std::ceil(layoutSize.height);
1529 // Nothing else do if there are no glyphs to layout.
1533 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1534 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1535 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1537 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1538 // This extra line needs to be removed.
1539 if(0u != lines.Count())
1541 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1543 if((0u == lastLine->characterRun.numberOfCharacters) &&
1544 (lastGlyphPlusOne == totalNumberOfGlyphs))
1546 lines.Remove(lastLine);
1550 // Retrieve BiDi info.
1551 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1553 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1554 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1555 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1557 // Set the layout bidirectional paramters.
1558 LayoutBidiParameters layoutBidiParameters;
1560 // Whether the layout is being updated or set from scratch.
1561 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1563 Vector2* glyphPositionsBuffer = nullptr;
1564 Vector<Vector2> newGlyphPositions;
1566 LineRun* linesBuffer = nullptr;
1567 Vector<LineRun> newLines;
1569 // Estimate the number of lines.
1570 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1571 Length numberOfLines = 0u;
1573 if(updateCurrentBuffer)
1575 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1576 glyphPositionsBuffer = newGlyphPositions.Begin();
1578 newLines.Resize(linesCapacity);
1579 linesBuffer = newLines.Begin();
1583 glyphPositionsBuffer = glyphPositions.Begin();
1585 lines.Resize(linesCapacity);
1586 linesBuffer = lines.Begin();
1589 float penY = CalculateLineOffset(lines,
1590 layoutParameters.startLineIndex);
1591 bool anyLineIsEliped = false;
1592 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1594 layoutBidiParameters.Clear();
1596 if(hasBidiParagraphs)
1598 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1600 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1601 endIt = bidirectionalParagraphsInfo.End();
1603 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1605 const BidirectionalParagraphInfoRun& run = *it;
1607 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1609 if(lastCharacterIndex <= startCharacterIndex)
1611 // Do not process, the paragraph has already been processed.
1615 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1617 layoutBidiParameters.paragraphDirection = run.direction;
1618 layoutBidiParameters.isBidirectional = true;
1621 // Has already been found.
1625 if(layoutBidiParameters.isBidirectional)
1627 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1628 endIt = bidirectionalLinesInfo.End();
1630 ++it, ++layoutBidiParameters.bidiLineIndex)
1632 const BidirectionalLineInfoRun& run = *it;
1634 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1636 if(lastCharacterIndex <= startCharacterIndex)
1642 if(startCharacterIndex < lastCharacterIndex)
1644 // Found where to insert the bidi line info.
1651 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1653 // Get the layout for the line.
1655 layout.direction = layoutBidiParameters.paragraphDirection;
1656 layout.glyphIndex = index;
1657 GetLineLayoutForBox(layoutParameters,
1658 layoutBidiParameters,
1665 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1666 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1667 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1668 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1669 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1671 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1673 // The width is too small and no characters are laid-out.
1674 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1676 lines.Resize(numberOfLines);
1678 // Rounds upward to avoid a non integer size.
1679 layoutSize.height = std::ceil(layoutSize.height);
1684 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1686 penY += layout.ascender;
1688 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1690 bool ellipsis = false;
1691 if(elideTextEnabled)
1693 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1695 // Does the ellipsis of the last line.
1696 ellipsis = EllipsisLine(layoutParameters,
1697 layoutBidiParameters,
1701 glyphPositionsBuffer,
1704 isAutoScrollEnabled,
1709 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1711 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1712 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1714 ellipsis = EllipsisLine(layoutParameters,
1715 layoutBidiParameters,
1719 glyphPositionsBuffer,
1722 isAutoScrollEnabled,
1727 //clear hyphen from ellipsis line
1728 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1729 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1731 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1733 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1734 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1738 // No more lines to layout.
1743 //In START location of ellipsis whether to shift lines or not.
1744 anyLineIsEliped |= ellipsis;
1746 // Whether the last line has been laid-out.
1747 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1749 if(numberOfLines == linesCapacity)
1751 // Reserve more space for the next lines.
1752 linesBuffer = ResizeLinesBuffer(lines,
1755 updateCurrentBuffer);
1758 // Updates the current text's layout with the line's layout.
1759 UpdateTextLayout(layoutParameters,
1767 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1769 if((nextIndex == totalNumberOfGlyphs) &&
1770 layoutParameters.isLastNewParagraph &&
1771 (mLayout == MULTI_LINE_BOX))
1773 // The last character of the text is a new paragraph character.
1774 // An extra line with no characters is added to increase the text's height
1775 // in order to place the cursor.
1777 if(numberOfLines == linesCapacity)
1779 // Reserve more space for the next lines.
1780 linesBuffer = ResizeLinesBuffer(lines,
1783 updateCurrentBuffer);
1786 UpdateTextLayout(layoutParameters,
1787 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1788 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1792 } // whether to add a last line.
1794 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1796 if((nullptr != bidirectionalLineInfo) &&
1797 !bidirectionalLineInfo->isIdentity &&
1798 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1800 SetGlyphPositions(layoutParameters,
1801 glyphPositionsBuffer,
1802 layoutBidiParameters,
1807 // Sets the positions of the glyphs.
1808 SetGlyphPositions(layoutParameters,
1809 glyphPositionsBuffer,
1813 // Updates the vertical pen's position.
1814 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1815 // If there is a defaultLineSize, updates the pen's position.
1816 if(mDefaultLineSize > 0.f)
1818 float lineSpacing = mDefaultLineSize - (layout.ascender + -layout.descender);
1819 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
1820 penY += lineSpacing;
1823 // Increase the glyph index.
1826 } // end for() traversing glyphs.
1828 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1829 if(anyLineIsEliped && numberOfLines > 1u)
1831 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1833 Length lineIndex = 0;
1834 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1836 LineRun& delLine = linesBuffer[lineIndex];
1837 delLine.ellipsis = true;
1839 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1840 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1842 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1843 linesBuffer[lineIndex].ellipsis = false;
1847 linesBuffer[0u].ellipsis = true;
1849 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1851 Length middleLineIndex = (numberOfLines) / 2u;
1852 Length ellipsisLineIndex = 0u;
1853 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1855 LineRun& delLine = linesBuffer[middleLineIndex];
1856 delLine.ellipsis = true;
1858 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1859 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1861 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1862 linesBuffer[lineIndex].ellipsis = false;
1865 ellipsisLineIndex = middleLineIndex - 1u;
1866 middleLineIndex = (numberOfLines) / 2u;
1869 linesBuffer[ellipsisLineIndex].ellipsis = true;
1873 if(updateCurrentBuffer)
1875 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1876 newGlyphPositions.Begin(),
1877 newGlyphPositions.End());
1878 glyphPositions.Resize(totalNumberOfGlyphs);
1880 newLines.Resize(numberOfLines);
1882 // Current text's layout size adds only the newly laid-out lines.
1883 // Updates the layout size with the previously laid-out lines.
1884 UpdateLayoutSize(lines,
1887 if(0u != newLines.Count())
1889 const LineRun& lastLine = *(newLines.End() - 1u);
1891 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1892 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1894 // Update the indices of the runs before the new laid-out lines are inserted.
1895 UpdateLineIndexOffsets(layoutParameters,
1900 // Insert the lines.
1901 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1908 lines.Resize(numberOfLines);
1911 // Rounds upward to avoid a non integer size.
1912 layoutSize.height = std::ceil(layoutSize.height);
1914 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1919 void Align(const Size& size,
1920 CharacterIndex startIndex,
1921 Length numberOfCharacters,
1922 Text::HorizontalAlignment::Type horizontalAlignment,
1923 Vector<LineRun>& lines,
1924 float& alignmentOffset,
1925 Dali::LayoutDirection::Type layoutDirection,
1926 bool matchLayoutDirection)
1928 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1930 alignmentOffset = MAX_FLOAT;
1931 // Traverse all lines and align the glyphs.
1932 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1936 LineRun& line = *it;
1938 if(line.characterRun.characterIndex < startIndex)
1940 // Do not align lines which have already been aligned.
1944 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1946 // Do not align lines beyond the last laid-out character.
1950 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1952 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1956 // Calculate the line's alignment offset accordingly with the align option,
1957 // the box width, line length, and the paragraph's direction.
1958 CalculateHorizontalAlignment(size.width,
1959 horizontalAlignment,
1962 matchLayoutDirection);
1964 // Updates the alignment offset.
1965 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
1969 void CalculateHorizontalAlignment(float boxWidth,
1970 HorizontalAlignment::Type horizontalAlignment,
1972 Dali::LayoutDirection::Type layoutDirection,
1973 bool matchLayoutDirection)
1975 line.alignmentOffset = 0.f;
1976 const bool isLineRTL = RTL == line.direction;
1978 // Whether to swap the alignment.
1979 // Swap if the line is RTL and is not required to match the direction of the system's language or if it's required to match the direction of the system's language and it's RTL.
1980 bool isLayoutRTL = isLineRTL;
1981 float lineLength = line.width;
1983 // match align for system language direction
1984 if(matchLayoutDirection)
1986 // Swap the alignment type if the line is right to left.
1987 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1989 // Calculate the horizontal line offset.
1990 switch(horizontalAlignment)
1992 case HorizontalAlignment::BEGIN:
1998 lineLength += line.extraLength;
2001 line.alignmentOffset = boxWidth - lineLength;
2005 line.alignmentOffset = 0.f;
2009 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2010 line.alignmentOffset -= line.extraLength;
2015 case HorizontalAlignment::CENTER:
2017 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2021 line.alignmentOffset -= line.extraLength;
2024 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2027 case HorizontalAlignment::END:
2031 line.alignmentOffset = 0.f;
2035 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2036 line.alignmentOffset -= line.extraLength;
2043 lineLength += line.extraLength;
2046 line.alignmentOffset = boxWidth - lineLength;
2053 void Initialize(LineRun& line)
2055 line.glyphRun.glyphIndex = 0u;
2056 line.glyphRun.numberOfGlyphs = 0u;
2057 line.characterRun.characterIndex = 0u;
2058 line.characterRun.numberOfCharacters = 0u;
2060 line.ascender = 0.f;
2061 line.descender = 0.f;
2062 line.extraLength = 0.f;
2063 line.alignmentOffset = 0.f;
2064 line.direction = LTR;
2065 line.ellipsis = false;
2066 line.lineSpacing = mDefaultLineSpacing;
2067 line.isSplitToTwoHalves = false;
2068 line.glyphRunSecondHalf.glyphIndex = 0u;
2069 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2070 line.characterRunForSecondHalfLine.characterIndex = 0u;
2071 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2076 float mDefaultLineSpacing;
2077 float mDefaultLineSize;
2079 IntrusivePtr<Metrics> mMetrics;
2085 mImpl = new Engine::Impl();
2093 void Engine::SetMetrics(MetricsPtr& metrics)
2095 mImpl->mMetrics = metrics;
2098 void Engine::SetLayout(Type layout)
2100 mImpl->mLayout = layout;
2103 Engine::Type Engine::GetLayout() const
2105 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2106 return mImpl->mLayout;
2109 void Engine::SetCursorWidth(int width)
2111 mImpl->mCursorWidth = static_cast<float>(width);
2114 int Engine::GetCursorWidth() const
2116 return static_cast<int>(mImpl->mCursorWidth);
2119 bool Engine::LayoutText(Parameters& layoutParameters,
2121 bool elideTextEnabled,
2122 bool& isAutoScrollEnabled,
2123 DevelText::EllipsisPosition::Type ellipsisPosition)
2125 return mImpl->LayoutText(layoutParameters,
2128 isAutoScrollEnabled,
2132 void Engine::Align(const Size& size,
2133 CharacterIndex startIndex,
2134 Length numberOfCharacters,
2135 Text::HorizontalAlignment::Type horizontalAlignment,
2136 Vector<LineRun>& lines,
2137 float& alignmentOffset,
2138 Dali::LayoutDirection::Type layoutDirection,
2139 bool matchLayoutDirection)
2144 horizontalAlignment,
2148 matchLayoutDirection);
2151 void Engine::SetDefaultLineSpacing(float lineSpacing)
2153 mImpl->mDefaultLineSpacing = lineSpacing;
2156 float Engine::GetDefaultLineSpacing() const
2158 return mImpl->mDefaultLineSpacing;
2161 void Engine::SetDefaultLineSize(float lineSize)
2163 mImpl->mDefaultLineSize = lineSize;
2166 float Engine::GetDefaultLineSize() const
2168 return mImpl->mDefaultLineSize;
2171 } // namespace Layout
2175 } // namespace Toolkit