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, bool isLastLine)
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 // In case this is the only/last line then line spacing should be ignored.
45 float lineHeight = lineRun.ascender - lineRun.descender;
47 if(!isLastLine || lineRun.lineSpacing > 0)
49 lineHeight += lineRun.lineSpacing;
58 #if defined(DEBUG_ENABLED)
59 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
62 const float MAX_FLOAT = std::numeric_limits<float>::max();
63 const CharacterDirection LTR = false;
64 const CharacterDirection RTL = !LTR;
65 const float LINE_SPACING = 0.f;
66 const float MIN_LINE_SIZE = 0.f;
67 const Character HYPHEN_UNICODE = 0x002D;
68 const float RELATIVE_LINE_SIZE = 1.f;
70 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
72 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
78 * @brief Stores temporary layout info of the line.
86 numberOfCharacters{0u},
93 whiteSpaceLengthEndOfLine{0.f},
95 isSplitToTwoHalves(false),
96 glyphIndexInSecondHalfLine{0u},
97 characterIndexInSecondHalfLine{0u},
98 numberOfGlyphsInSecondHalfLine{0u},
99 numberOfCharactersInSecondHalfLine{0u}
113 numberOfCharacters = 0u;
114 ascender = -MAX_FLOAT;
115 descender = MAX_FLOAT;
117 isSplitToTwoHalves = false;
118 glyphIndexInSecondHalfLine = 0u;
119 characterIndexInSecondHalfLine = 0u;
120 numberOfGlyphsInSecondHalfLine = 0u;
121 numberOfCharactersInSecondHalfLine = 0u;
124 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
125 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
126 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
127 Length numberOfCharacters; ///< The number of characters which fit in one line.
128 float ascender; ///< The maximum ascender of all fonts in the line.
129 float descender; ///< The minimum descender of all fonts in the line.
130 float lineSpacing; ///< The line spacing
131 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
132 float previousAdvance; ///< The advance of the previous glyph.
133 float length; ///< The current length of the line.
134 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
135 CharacterDirection direction;
137 bool isSplitToTwoHalves; ///< Whether the second half is defined.
138 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
139 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
140 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
141 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
144 struct LayoutBidiParameters
148 paragraphDirection = LTR;
149 bidiParagraphIndex = 0u;
151 isBidirectional = false;
154 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
155 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
156 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
157 bool isBidirectional = false; ///< Whether the text is bidirectional.
163 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
165 mDefaultLineSpacing{LINE_SPACING},
166 mDefaultLineSize{MIN_LINE_SIZE},
167 mRelativeLineSize{RELATIVE_LINE_SIZE}
172 * @brief get the line spacing.
174 * @param[in] textSize The text size.
175 * @return the line spacing value.
177 float GetLineSpacing(float textSize)
182 // Sets the line size
183 lineSpacing = mDefaultLineSize - textSize;
184 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
186 // Add the line spacing
187 lineSpacing += mDefaultLineSpacing;
189 //subtract line spcaing if relativeLineSize < 1 & larger than min height
190 relTextSize = textSize * mRelativeLineSize;
191 if(relTextSize > mDefaultLineSize)
193 if(mRelativeLineSize < 1)
195 //subtract the difference (always will be positive)
196 lineSpacing -= (textSize - relTextSize);
200 //reverse the addition in the top.
201 if(mDefaultLineSize > textSize)
203 lineSpacing -= mDefaultLineSize - textSize;
206 //add difference instead
207 lineSpacing += relTextSize - textSize;
215 * @brief Updates the line ascender and descender with the metrics of a new font.
217 * @param[in] glyphMetrics The metrics of the new font.
218 * @param[in,out] lineLayout The line layout.
220 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
222 Text::FontMetrics fontMetrics;
223 if(0u != glyphMetrics.fontId)
225 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
229 fontMetrics.ascender = glyphMetrics.fontHeight;
230 fontMetrics.descender = 0.f;
231 fontMetrics.height = fontMetrics.ascender;
232 fontMetrics.underlinePosition = 0.f;
233 fontMetrics.underlineThickness = 1.f;
236 // Sets the maximum ascender.
237 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
239 // Sets the minimum descender.
240 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
242 lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender);
246 * @brief Merges a temporary line layout into the line layout.
248 * @param[in,out] lineLayout The line layout.
249 * @param[in] tmpLineLayout A temporary line layout.
250 * @param[in] isShifted Whether to shift first glyph and character indices.
252 void MergeLineLayout(LineLayout& lineLayout,
253 const LineLayout& tmpLineLayout,
256 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
257 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
259 lineLayout.penX = tmpLineLayout.penX;
260 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
262 lineLayout.length = tmpLineLayout.length;
263 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
265 // Sets the maximum ascender.
266 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
268 // Sets the minimum descender.
269 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
271 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
274 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
275 lineLayout.characterIndex = tmpLineLayout.characterIndex;
278 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
279 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
280 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
281 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
282 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
285 void LayoutRightToLeft(const Parameters& parameters,
286 const BidirectionalLineInfoRun& bidirectionalLineInfo,
288 float& whiteSpaceLengthEndOfLine)
290 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
291 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
292 // then the first half of line "characterRun",
293 // Otherwise travers whole characters in"characterRun".
295 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
296 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
297 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
298 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
300 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
301 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
302 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
304 CharacterIndex characterLogicalIndex = 0u;
305 CharacterIndex characterVisualIndex = 0u;
307 float calculatedAdvance = 0.f;
309 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
310 // Otherwise maps the first visual index from visualToLogicalMap.
311 // This is to initialize the first visual index.
312 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
314 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
318 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
321 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
323 if(RTL == bidirectionalLineInfo.direction)
325 // If there are characters in the second half of Line.
326 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
328 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
329 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
331 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
333 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
334 whiteSpaceLengthEndOfLine += calculatedAdvance;
336 ++characterLogicalIndex;
337 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
341 // If all characters in the second half of Line are WhiteSpaces.
342 // then continue adding the WhiteSpaces from the first hel of Line.
343 // Also this is valid when the line was not splitted.
344 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
346 extendedToSecondHalf = true; // Whether the logical index is extended to second half
347 characterLogicalIndex = 0u;
348 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
350 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
351 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
353 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
355 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
356 whiteSpaceLengthEndOfLine += calculatedAdvance;
358 ++characterLogicalIndex;
359 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
364 // Here's the first index of character is not WhiteSpace
365 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
367 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
368 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
369 lastGlyphOfParagraphPlusOne,
370 charactersPerGlyphBuffer);
372 GlyphMetrics glyphMetrics;
373 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
374 GetGlyphsMetrics(glyphIndex,
375 numberOfGLyphsInGroup,
381 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
383 // Traverses the characters of the right to left paragraph.
384 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
385 if(!extendedToSecondHalf &&
386 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
388 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
390 // Convert the character in the logical order into the character in the visual order.
391 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
392 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
394 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
396 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
397 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
398 lastGlyphOfParagraphPlusOne,
399 charactersPerGlyphBuffer);
401 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
403 GlyphMetrics glyphMetrics;
404 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
405 GetGlyphsMetrics(glyphIndex,
406 numberOfGLyphsInGroup,
414 // If glyph is WhiteSpace then:
415 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
416 // the endOfLine in RTL was the headOfLine for layouting.
417 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
418 if(RTL == bidirectionalLineInfo.direction)
420 length += glyphMetrics.advance;
424 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
426 penX += glyphMetrics.advance;
430 // If glyph is not whiteSpace then:
431 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
432 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
433 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
434 // Otherwise the current length is maximum.
435 if(LTR == bidirectionalLineInfo.direction)
437 whiteSpaceLengthEndOfLine = 0.f;
439 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
440 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
445 // Continue traversing in the first half of line or in the whole line.
446 // If the second half of line was extended then continue from logical index in the first half of line
447 // Also this is valid when the line was not splitted and there were WhiteSpace.
448 // Otherwise start from first logical index in line.
449 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
450 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
452 // Convert the character in the logical order into the character in the visual order.
453 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
454 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
456 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
458 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
459 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
460 lastGlyphOfParagraphPlusOne,
461 charactersPerGlyphBuffer);
463 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
465 GlyphMetrics glyphMetrics;
466 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
467 GetGlyphsMetrics(glyphIndex,
468 numberOfGLyphsInGroup,
476 // If glyph is WhiteSpace then:
477 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
478 // the endOfLine in RTL was the headOfLine for layouting.
479 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
480 if(RTL == bidirectionalLineInfo.direction)
482 length += glyphMetrics.advance;
486 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
488 penX += glyphMetrics.advance;
492 // If glyph is not whiteSpace then:
493 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
494 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
495 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
496 // Otherwise the current length is maximum.
497 if(LTR == bidirectionalLineInfo.direction)
499 whiteSpaceLengthEndOfLine = 0.f;
501 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
502 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
507 void ReorderBiDiLayout(const Parameters& parameters,
508 LayoutBidiParameters& bidiParameters,
509 const LineLayout& currentLineLayout,
510 LineLayout& lineLayout,
511 bool breakInCharacters,
512 bool enforceEllipsisInSingleLine)
514 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
516 // The last glyph to be laid-out.
517 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
519 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
521 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
522 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
523 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
525 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
527 // Sets the visual to logical map tables needed to reorder the text.
528 ReorderLine(bidirectionalParagraphInfo,
529 bidirectionalLinesInfo,
530 bidiParameters.bidiLineIndex,
531 lineLayout.characterIndex,
532 lineLayout.numberOfCharacters,
533 lineLayout.characterIndexInSecondHalfLine,
534 lineLayout.numberOfCharactersInSecondHalfLine,
535 bidiParameters.paragraphDirection);
537 // Recalculate the length of the line and update the layout.
538 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
540 if(!bidirectionalLineInfo.isIdentity)
543 float whiteSpaceLengthEndOfLine = 0.f;
544 LayoutRightToLeft(parameters,
545 bidirectionalLineInfo,
547 whiteSpaceLengthEndOfLine);
549 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
550 if(!Equals(length, lineLayout.length))
552 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
554 if(isMultiline && (length > parameters.boundingBox.width))
556 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
558 // The word doesn't fit in one line. It has to be split by character.
560 // Remove the last laid out glyph(s) as they doesn't fit.
561 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
563 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
564 lastGlyphOfParagraphPlusOne,
565 charactersPerGlyphBuffer);
567 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
569 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
570 lineLayout.numberOfCharacters -= numberOfCharacters;
572 AdjustLayout(parameters,
574 bidirectionalParagraphInfo,
577 if(lineLayout.length < parameters.boundingBox.width)
582 if(glyphIndex < numberOfGLyphsInGroup)
584 // avoids go under zero for an unsigned int.
588 glyphIndex -= numberOfGLyphsInGroup;
593 lineLayout = currentLineLayout;
595 AdjustLayout(parameters,
597 bidirectionalParagraphInfo,
603 lineLayout.length = std::max(length, lineLayout.length);
610 void AdjustLayout(const Parameters& parameters,
611 LayoutBidiParameters& bidiParameters,
612 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
613 LineLayout& lineLayout)
615 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
617 // Remove current reordered line.
618 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
620 // Re-build the conversion table without the removed glyphs.
621 ReorderLine(bidirectionalParagraphInfo,
622 bidirectionalLinesInfo,
623 bidiParameters.bidiLineIndex,
624 lineLayout.characterIndex,
625 lineLayout.numberOfCharacters,
626 lineLayout.characterIndexInSecondHalfLine,
627 lineLayout.numberOfCharactersInSecondHalfLine,
628 bidiParameters.paragraphDirection);
630 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
633 float whiteSpaceLengthEndOfLine = 0.f;
634 LayoutRightToLeft(parameters,
635 bidirectionalLineInfo,
637 whiteSpaceLengthEndOfLine);
639 lineLayout.length = length;
640 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
644 * Retrieves the line layout for a given box width.
646 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
647 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
648 * to reorder the line and recalculate its length.
651 * @param[in] parameters The layout parameters.
652 * @param[] bidiParameters Bidirectional info for the current line.
653 * @param[out] lineLayout The line layout.
654 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
655 * @param[in] ellipsisPosition Where is the location the text elide
657 void GetLineLayoutForBox(const Parameters& parameters,
658 LayoutBidiParameters& bidiParameters,
659 LineLayout& lineLayout,
661 DevelText::EllipsisPosition::Type ellipsisPosition,
662 bool enforceEllipsisInSingleLine,
663 bool elideTextEnabled)
665 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
666 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
668 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
669 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
670 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
671 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
672 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
674 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
675 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
677 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
678 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
679 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
680 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
681 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
682 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
684 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
686 // The last glyph to be laid-out.
687 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
689 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
690 // In the case the line starts with a right to left character, if the width is longer than the advance,
691 // the difference needs to be added to the line length.
693 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
694 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
695 lastGlyphOfParagraphPlusOne,
696 charactersPerGlyphBuffer);
698 float targetWidth = parameters.boundingBox.width;
699 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
701 bool isSecondHalf = false;
703 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
704 float calculatedAdvance = 0.f;
705 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
706 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
708 GlyphMetrics glyphMetrics;
709 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
710 GetGlyphsMetrics(lineLayout.glyphIndex,
711 numberOfGLyphsInGroup,
717 // Set the direction of the first character of the line.
718 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
720 // Stores temporary line layout which has not been added to the final line layout.
721 LineLayout tmpLineLayout;
723 // Initialize the start point.
725 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
726 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
727 // 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.
728 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
730 // Calculate the line height if there is no characters.
731 FontId lastFontId = glyphMetrics.fontId;
732 UpdateLineHeight(glyphMetrics, tmpLineLayout);
734 bool oneWordLaidOut = false;
735 bool oneHyphenLaidOut = false;
736 GlyphIndex hyphenIndex = 0;
737 GlyphInfo hyphenGlyph;
739 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
740 glyphIndex < lastGlyphOfParagraphPlusOne;)
742 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
744 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
745 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
746 lastGlyphOfParagraphPlusOne,
747 charactersPerGlyphBuffer);
749 GlyphMetrics glyphMetrics;
750 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
751 GetGlyphsMetrics(glyphIndex,
752 numberOfGLyphsInGroup,
758 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
760 // Check if the font of the current glyph is the same of the previous one.
761 // If it's different the ascender and descender need to be updated.
762 if(lastFontId != glyphMetrics.fontId)
764 UpdateLineHeight(glyphMetrics, tmpLineLayout);
765 lastFontId = glyphMetrics.fontId;
768 // Get the character indices for the current glyph. The last character index is needed
769 // because there are glyphs formed by more than one character but their break info is
770 // given only for the last character.
771 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
772 const bool hasCharacters = charactersPerGlyph > 0u;
773 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
774 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
776 // Get the line break info for the current character.
777 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
781 // Increase the number of characters.
782 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
784 // Increase the number of glyphs.
785 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
789 // Increase the number of characters.
790 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
792 // Increase the number of glyphs.
793 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
796 // Check whether is a white space.
797 const Character character = *(textBuffer + characterFirstIndex);
798 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
800 // Calculate the length of the line.
802 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
803 const float previousTmpPenX = tmpLineLayout.penX;
804 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
805 const float previousTmpLength = tmpLineLayout.length;
806 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
810 // Add the length to the length of white spaces at the end of the line.
811 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
812 // The advance is used as the width is always zero for the white spaces.
816 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
817 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
819 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
821 // Clear the white space length at the end of the line.
822 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
825 if(isSplitToTwoHalves && (!isSecondHalf) &&
826 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
828 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
829 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
831 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
832 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
834 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
835 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
837 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
839 // Check if the accumulated length fits in the width of the box.
840 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
841 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
842 completelyFill && !isMultiline &&
843 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
845 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
847 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
849 GlyphMetrics glyphMetrics;
850 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
851 GetGlyphsMetrics(glyphIndexToRemove,
852 numberOfGLyphsInGroup,
858 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
859 lastGlyphOfParagraphPlusOne,
860 charactersPerGlyphBuffer);
862 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
863 const bool hasCharacters = charactersPerGlyph > 0u;
864 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
865 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
867 // Check whether is a white space.
868 const Character character = *(textBuffer + characterFirstIndex);
869 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
873 // Decrease the number of characters for SecondHalf.
874 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
876 // Decrease the number of glyphs for SecondHalf.
877 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
881 // Decrease the number of characters.
882 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
884 // Decrease the number of glyphs.
885 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
888 if(isRemovedGlyphWhiteSpace)
890 tmpLineLayout.penX -= glyphMetrics.advance;
891 tmpLineLayout.length -= glyphMetrics.advance;
895 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
896 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
901 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
902 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
903 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
907 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
908 tmpLineLayout.characterIndex = characterLastIndex + 1u;
909 glyphIndexToRemove = tmpLineLayout.glyphIndex;
913 else if((completelyFill || isMultiline) &&
914 (tmpLineLayout.length > targetWidth))
916 // Current word does not fit in the box's width.
917 if(((oneHyphenLaidOut && isHyphenMode) ||
918 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
921 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
922 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
925 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
927 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
929 // The word doesn't fit in the control's width. It needs to be split by character.
930 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
934 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
935 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
939 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
940 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
943 tmpLineLayout.penX = previousTmpPenX;
944 tmpLineLayout.previousAdvance = previousTmpAdvance;
945 tmpLineLayout.length = previousTmpLength;
946 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
949 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
951 // Add part of the word to the line layout and shift the first glyph.
952 MergeLineLayout(lineLayout, tmpLineLayout, true);
954 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
955 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
957 // Add part of the word to the line layout.
958 MergeLineLayout(lineLayout, tmpLineLayout, false);
963 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
966 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
968 // Reorder the RTL line.
969 if(bidiParameters.isBidirectional)
971 ReorderBiDiLayout(parameters,
976 enforceEllipsisInSingleLine);
982 if((isMultiline || isLastGlyph) &&
983 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
985 LineLayout currentLineLayout = lineLayout;
986 oneHyphenLaidOut = false;
988 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
990 // Must break the line. Update the line layout, shift the first glyph and return.
991 MergeLineLayout(lineLayout, tmpLineLayout, true);
995 // Must break the line. Update the line layout and return.
996 MergeLineLayout(lineLayout, tmpLineLayout, false);
999 // Reorder the RTL line.
1000 if(bidiParameters.isBidirectional)
1002 ReorderBiDiLayout(parameters,
1007 enforceEllipsisInSingleLine);
1010 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
1011 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1017 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1019 oneHyphenLaidOut = false;
1020 oneWordLaidOut = isWordLaidOut;
1021 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
1023 // Current glyph is the last one of the current word.
1024 // Add the temporal layout to the current one.
1025 MergeLineLayout(lineLayout, tmpLineLayout, false);
1027 tmpLineLayout.Clear();
1031 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1032 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1034 hyphenGlyph = GlyphInfo();
1035 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1037 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1038 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1040 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1042 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1044 hyphenIndex = glyphIndex;
1045 oneHyphenLaidOut = true;
1047 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1049 // Current glyph is the last one of the current word hyphen.
1050 // Add the temporal layout to the current one.
1051 MergeLineLayout(lineLayout, tmpLineLayout, false);
1053 tmpLineLayout.Clear();
1057 glyphIndex += numberOfGLyphsInGroup;
1060 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1063 void SetGlyphPositions(const Parameters& layoutParameters,
1064 Vector2* glyphPositionsBuffer,
1065 const LineLayout& layout)
1067 // Traverse the glyphs and set the positions.
1069 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1070 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1071 const Length numberOfGlyphs = layout.numberOfGlyphs;
1072 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1074 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1075 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1077 // Check if the x bearing of the first character is negative.
1078 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1079 // so the penX position needs to be moved to the right.
1080 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1081 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1083 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1084 layoutParameters.textModel->mLogicalModel,
1085 interGlyphExtraAdvance,
1087 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1088 startIndexForGlyphPositions,
1089 glyphPositionsBuffer,
1092 if(layout.isSplitToTwoHalves)
1094 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1095 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1096 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1098 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1099 layoutParameters.textModel->mLogicalModel,
1100 interGlyphExtraAdvance,
1101 numberOfGlyphsInSecondHalfLine,
1102 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1103 startIndexForGlyphPositionsnSecondHalf,
1104 glyphPositionsBuffer,
1109 void SetGlyphPositions(const Parameters& layoutParameters,
1110 Vector2* glyphPositionsBuffer,
1111 LayoutBidiParameters& layoutBidiParameters,
1112 const LineLayout& layout)
1114 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1115 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1116 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1118 CharacterIndex characterLogicalIndex = 0u;
1119 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1120 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1124 if(layout.isSplitToTwoHalves)
1126 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1127 layoutParameters.textModel->mLogicalModel,
1128 layoutBidiParameters.bidiLineIndex,
1129 layoutParameters.startGlyphIndex,
1130 glyphPositionsBuffer,
1131 characterVisualIndex,
1132 characterLogicalIndex,
1136 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1138 extendedToSecondHalf = true;
1139 characterLogicalIndex = 0u;
1140 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1142 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1143 layoutParameters.textModel->mLogicalModel,
1144 layoutBidiParameters.bidiLineIndex,
1145 layoutParameters.startGlyphIndex,
1146 glyphPositionsBuffer,
1147 characterVisualIndex,
1148 characterLogicalIndex,
1152 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1153 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1155 penX += -glyph.xBearing;
1157 // Traverses the characters of the right to left paragraph.
1158 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1160 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1161 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1162 layoutParameters.startGlyphIndex,
1163 layoutParameters.interGlyphExtraAdvance,
1164 bidiLine.characterRunForSecondHalfLine,
1165 bidiLine.visualToLogicalMapSecondHalf,
1166 glyphPositionsBuffer,
1167 characterLogicalIndex,
1171 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1173 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1174 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1175 layoutParameters.startGlyphIndex,
1176 layoutParameters.interGlyphExtraAdvance,
1177 bidiLine.characterRun,
1178 bidiLine.visualToLogicalMap,
1179 glyphPositionsBuffer,
1180 characterLogicalIndex,
1185 * @brief Resizes the line buffer.
1187 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1188 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1189 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1190 * @param[in] updateCurrentBuffer Whether the layout is updated.
1192 * @return Pointer to either lines or newLines.
1194 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1195 Vector<LineRun>& newLines,
1196 Length& linesCapacity,
1197 bool updateCurrentBuffer)
1199 LineRun* linesBuffer = nullptr;
1200 // Reserve more space for the next lines.
1201 linesCapacity *= 2u;
1202 if(updateCurrentBuffer)
1204 newLines.Resize(linesCapacity);
1205 linesBuffer = newLines.Begin();
1209 lines.Resize(linesCapacity);
1210 linesBuffer = lines.Begin();
1217 * Ellipsis a line if it exceeds the width's of the bounding box.
1219 * @param[in] layoutParameters The parameters needed to layout the text.
1220 * @param[in] layout The line layout.
1221 * @param[in,out] layoutSize The text's layout size.
1222 * @param[in,out] linesBuffer Pointer to the line's buffer.
1223 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1224 * @param[in,out] numberOfLines The number of laid-out lines.
1225 * @param[in] penY The vertical layout position.
1226 * @param[in] currentParagraphDirection The current paragraph's direction.
1227 * @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
1228 * @param[in] ellipsisPosition Where is the location the text elide
1230 * return Whether the line is ellipsized.
1232 bool EllipsisLine(const Parameters& layoutParameters,
1233 LayoutBidiParameters& layoutBidiParameters,
1234 const LineLayout& layout,
1236 LineRun* linesBuffer,
1237 Vector2* glyphPositionsBuffer,
1238 Length& numberOfLines,
1240 bool& isAutoScrollEnabled,
1241 DevelText::EllipsisPosition::Type ellipsisPosition,
1242 bool enforceEllipsisInSingleLine)
1244 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))));
1245 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1246 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1248 isAutoScrollEnabled = false;
1249 // Do not layout more lines if ellipsis is enabled.
1251 // The last line needs to be completely filled with characters.
1252 // Part of a word may be used.
1254 LineRun* lineRun = nullptr;
1255 LineLayout ellipsisLayout;
1256 if(0u != numberOfLines)
1258 // Get the last line and layout it again with the 'completelyFill' flag to true.
1259 lineRun = linesBuffer + (numberOfLines - 1u);
1260 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1262 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1266 // At least there is space reserved for one line.
1267 lineRun = linesBuffer;
1269 lineRun->glyphRun.glyphIndex = 0u;
1270 ellipsisLayout.glyphIndex = 0u;
1271 lineRun->isSplitToTwoHalves = false;
1276 GetLineLayoutForBox(layoutParameters,
1277 layoutBidiParameters,
1281 enforceEllipsisInSingleLine,
1284 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1286 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1289 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1290 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1291 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1292 lineRun->width = ellipsisLayout.length;
1293 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1294 lineRun->ascender = ellipsisLayout.ascender;
1295 lineRun->descender = ellipsisLayout.descender;
1296 lineRun->ellipsis = true;
1298 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1299 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1300 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1301 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1302 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1304 layoutSize.width = layoutParameters.boundingBox.width;
1305 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1307 layoutSize.height += GetLineHeight(*lineRun, true);
1310 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1312 if(layoutBidiParameters.isBidirectional)
1314 layoutBidiParameters.bidiLineIndex = 0u;
1315 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1316 endIt = bidirectionalLinesInfo.End();
1318 ++it, ++layoutBidiParameters.bidiLineIndex)
1320 const BidirectionalLineInfoRun& run = *it;
1321 //To handle case when the laid characters exist in next line.
1322 //More than one BidirectionalLineInfoRun could start with same character.
1323 //When need to check also numberOfCharacters in line.
1324 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1325 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1326 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1327 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1328 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1330 // Found where to insert the bidi line info.
1336 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1338 if((nullptr != bidirectionalLineInfo) &&
1339 !bidirectionalLineInfo->isIdentity &&
1340 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1342 lineRun->direction = RTL;
1343 SetGlyphPositions(layoutParameters,
1344 glyphPositionsBuffer,
1345 layoutBidiParameters,
1350 lineRun->direction = LTR;
1351 SetGlyphPositions(layoutParameters,
1352 glyphPositionsBuffer,
1361 * @brief Updates the text layout with a new laid-out line.
1363 * @param[in] layoutParameters The parameters needed to layout the text.
1364 * @param[in] layout The line layout.
1365 * @param[in,out] layoutSize The text's layout size.
1366 * @param[in,out] linesBuffer Pointer to the line's buffer.
1367 * @param[in] index Index to the vector of glyphs.
1368 * @param[in,out] numberOfLines The number of laid-out lines.
1369 * @param[in] isLastLine Whether the laid-out line is the last one.
1371 void UpdateTextLayout(const Parameters& layoutParameters,
1372 const LineLayout& layout,
1374 LineRun* linesBuffer,
1376 Length& numberOfLines,
1379 LineRun& lineRun = *(linesBuffer + numberOfLines);
1382 lineRun.glyphRun.glyphIndex = index;
1383 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1384 lineRun.characterRun.characterIndex = layout.characterIndex;
1385 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1386 lineRun.width = layout.length;
1387 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1389 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1390 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1391 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1392 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1393 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1395 // Rounds upward to avoid a non integer size.
1396 lineRun.width = std::ceil(lineRun.width);
1398 lineRun.ascender = layout.ascender;
1399 lineRun.descender = layout.descender;
1400 lineRun.direction = layout.direction;
1401 lineRun.ellipsis = false;
1403 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1405 // Update the actual size.
1406 if(lineRun.width > layoutSize.width)
1408 layoutSize.width = lineRun.width;
1411 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1415 * @brief Updates the text layout with the last laid-out line.
1417 * @param[in] layoutParameters The parameters needed to layout the text.
1418 * @param[in] characterIndex The character index of the line.
1419 * @param[in] glyphIndex The glyph index of the line.
1420 * @param[in,out] layoutSize The text's layout size.
1421 * @param[in,out] linesBuffer Pointer to the line's buffer.
1422 * @param[in,out] numberOfLines The number of laid-out lines.
1424 void UpdateTextLayout(const Parameters& layoutParameters,
1425 CharacterIndex characterIndex,
1426 GlyphIndex glyphIndex,
1428 LineRun* linesBuffer,
1429 Length& numberOfLines)
1431 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1433 // Need to add a new line with no characters but with height to increase the layoutSize.height
1434 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1436 Text::FontMetrics fontMetrics;
1437 if(0u != glyphInfo.fontId)
1439 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1442 LineRun& lineRun = *(linesBuffer + numberOfLines);
1445 lineRun.glyphRun.glyphIndex = glyphIndex;
1446 lineRun.glyphRun.numberOfGlyphs = 0u;
1447 lineRun.characterRun.characterIndex = characterIndex;
1448 lineRun.characterRun.numberOfCharacters = 0u;
1449 lineRun.width = 0.f;
1450 lineRun.ascender = fontMetrics.ascender;
1451 lineRun.descender = fontMetrics.descender;
1452 lineRun.extraLength = 0.f;
1453 lineRun.alignmentOffset = 0.f;
1454 lineRun.direction = LTR;
1455 lineRun.ellipsis = false;
1457 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1459 layoutSize.height += GetLineHeight(lineRun, true);
1463 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1465 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1466 * @param[in,out] layoutSize The text's layout size.
1468 void UpdateLayoutSize(const Vector<LineRun>& lines,
1471 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1472 endIt = lines.End();
1476 const LineRun& line = *it;
1477 bool isLastLine = (it + 1 == endIt);
1479 if(line.width > layoutSize.width)
1481 layoutSize.width = line.width;
1484 layoutSize.height += GetLineHeight(line, isLastLine);
1489 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1491 * @param[in] layoutParameters The parameters needed to layout the text.
1492 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1493 * @param[in] characterOffset The offset to be added to the runs of characters.
1494 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1496 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1497 Vector<LineRun>& lines,
1498 Length characterOffset,
1501 // Update the glyph and character runs.
1502 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1503 endIt = lines.End();
1507 LineRun& line = *it;
1509 line.glyphRun.glyphIndex = glyphOffset;
1510 line.characterRun.characterIndex = characterOffset;
1512 glyphOffset += line.glyphRun.numberOfGlyphs;
1513 characterOffset += line.characterRun.numberOfCharacters;
1517 bool LayoutText(Parameters& layoutParameters,
1519 bool elideTextEnabled,
1520 bool& isAutoScrollEnabled,
1521 DevelText::EllipsisPosition::Type ellipsisPosition)
1523 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1524 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1526 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1527 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1529 //Reset indices of ElidedGlyphs
1530 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1531 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1532 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1533 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1535 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1537 if(0u == layoutParameters.numberOfGlyphs)
1539 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1540 if(layoutParameters.isLastNewParagraph)
1542 Length numberOfLines = lines.Count();
1543 if(0u != numberOfLines)
1545 const LineRun& lastLine = *(lines.End() - 1u);
1547 if(0u != lastLine.characterRun.numberOfCharacters)
1549 // Need to add a new line with no characters but with height to increase the layoutSize.height
1551 Initialize(newLine);
1552 lines.PushBack(newLine);
1554 UpdateTextLayout(layoutParameters,
1555 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1556 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1564 // Calculates the layout size.
1565 UpdateLayoutSize(lines,
1568 // Rounds upward to avoid a non integer size.
1569 layoutSize.height = std::ceil(layoutSize.height);
1571 // Nothing else do if there are no glyphs to layout.
1575 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1576 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1577 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1579 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1580 // This extra line needs to be removed.
1581 if(0u != lines.Count())
1583 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1585 if((0u == lastLine->characterRun.numberOfCharacters) &&
1586 (lastGlyphPlusOne == totalNumberOfGlyphs))
1588 lines.Remove(lastLine);
1592 // Retrieve BiDi info.
1593 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1595 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1596 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1597 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1599 // Set the layout bidirectional paramters.
1600 LayoutBidiParameters layoutBidiParameters;
1602 // Whether the layout is being updated or set from scratch.
1603 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1605 Vector2* glyphPositionsBuffer = nullptr;
1606 Vector<Vector2> newGlyphPositions;
1608 LineRun* linesBuffer = nullptr;
1609 Vector<LineRun> newLines;
1611 // Estimate the number of lines.
1612 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1613 Length numberOfLines = 0u;
1615 if(updateCurrentBuffer)
1617 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1618 glyphPositionsBuffer = newGlyphPositions.Begin();
1620 newLines.Resize(linesCapacity);
1621 linesBuffer = newLines.Begin();
1625 glyphPositionsBuffer = glyphPositions.Begin();
1627 lines.Resize(linesCapacity);
1628 linesBuffer = lines.Begin();
1631 float penY = CalculateLineOffset(lines,
1632 layoutParameters.startLineIndex);
1633 bool anyLineIsEliped = false;
1634 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1636 layoutBidiParameters.Clear();
1638 if(hasBidiParagraphs)
1640 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1642 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1643 endIt = bidirectionalParagraphsInfo.End();
1645 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1647 const BidirectionalParagraphInfoRun& run = *it;
1649 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1651 if(lastCharacterIndex <= startCharacterIndex)
1653 // Do not process, the paragraph has already been processed.
1657 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1659 layoutBidiParameters.paragraphDirection = run.direction;
1660 layoutBidiParameters.isBidirectional = true;
1663 // Has already been found.
1667 if(layoutBidiParameters.isBidirectional)
1669 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1670 endIt = bidirectionalLinesInfo.End();
1672 ++it, ++layoutBidiParameters.bidiLineIndex)
1674 const BidirectionalLineInfoRun& run = *it;
1676 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1678 if(lastCharacterIndex <= startCharacterIndex)
1684 if(startCharacterIndex < lastCharacterIndex)
1686 // Found where to insert the bidi line info.
1693 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1695 // Get the layout for the line.
1697 layout.direction = layoutBidiParameters.paragraphDirection;
1698 layout.glyphIndex = index;
1699 GetLineLayoutForBox(layoutParameters,
1700 layoutBidiParameters,
1707 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1708 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1709 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1710 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1711 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1713 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1715 // The width is too small and no characters are laid-out.
1716 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1718 lines.Resize(numberOfLines);
1720 // Rounds upward to avoid a non integer size.
1721 layoutSize.height = std::ceil(layoutSize.height);
1726 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1728 penY += layout.ascender;
1730 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1732 bool ellipsis = false;
1733 if(elideTextEnabled)
1735 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1737 // Does the ellipsis of the last line.
1738 ellipsis = EllipsisLine(layoutParameters,
1739 layoutBidiParameters,
1743 glyphPositionsBuffer,
1746 isAutoScrollEnabled,
1751 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1753 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1754 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1756 ellipsis = EllipsisLine(layoutParameters,
1757 layoutBidiParameters,
1761 glyphPositionsBuffer,
1764 isAutoScrollEnabled,
1769 //clear hyphen from ellipsis line
1770 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1771 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1773 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1775 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1776 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1780 // No more lines to layout.
1785 //In START location of ellipsis whether to shift lines or not.
1786 anyLineIsEliped |= ellipsis;
1788 // Whether the last line has been laid-out.
1789 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1791 if(numberOfLines == linesCapacity)
1793 // Reserve more space for the next lines.
1794 linesBuffer = ResizeLinesBuffer(lines,
1797 updateCurrentBuffer);
1800 // Updates the current text's layout with the line's layout.
1801 UpdateTextLayout(layoutParameters,
1809 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1811 if((nextIndex == totalNumberOfGlyphs) &&
1812 layoutParameters.isLastNewParagraph &&
1813 (mLayout == MULTI_LINE_BOX))
1815 // The last character of the text is a new paragraph character.
1816 // An extra line with no characters is added to increase the text's height
1817 // in order to place the cursor.
1819 if(numberOfLines == linesCapacity)
1821 // Reserve more space for the next lines.
1822 linesBuffer = ResizeLinesBuffer(lines,
1825 updateCurrentBuffer);
1828 UpdateTextLayout(layoutParameters,
1829 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1830 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1834 } // whether to add a last line.
1836 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1838 if((nullptr != bidirectionalLineInfo) &&
1839 !bidirectionalLineInfo->isIdentity &&
1840 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1842 SetGlyphPositions(layoutParameters,
1843 glyphPositionsBuffer,
1844 layoutBidiParameters,
1849 // Sets the positions of the glyphs.
1850 SetGlyphPositions(layoutParameters,
1851 glyphPositionsBuffer,
1855 // Updates the vertical pen's position.
1856 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender);
1858 // Increase the glyph index.
1861 } // end for() traversing glyphs.
1863 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1864 if(anyLineIsEliped && numberOfLines > 1u)
1866 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1868 Length lineIndex = 0;
1869 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1871 LineRun& delLine = linesBuffer[lineIndex];
1872 delLine.ellipsis = true;
1874 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1875 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1877 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1878 linesBuffer[lineIndex].ellipsis = false;
1882 linesBuffer[0u].ellipsis = true;
1884 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1886 Length middleLineIndex = (numberOfLines) / 2u;
1887 Length ellipsisLineIndex = 0u;
1888 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1890 LineRun& delLine = linesBuffer[middleLineIndex];
1891 delLine.ellipsis = true;
1893 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1894 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1896 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1897 linesBuffer[lineIndex].ellipsis = false;
1900 ellipsisLineIndex = middleLineIndex - 1u;
1901 middleLineIndex = (numberOfLines) / 2u;
1904 linesBuffer[ellipsisLineIndex].ellipsis = true;
1908 if(updateCurrentBuffer)
1910 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1911 newGlyphPositions.Begin(),
1912 newGlyphPositions.End());
1913 glyphPositions.Resize(totalNumberOfGlyphs);
1915 newLines.Resize(numberOfLines);
1917 // Current text's layout size adds only the newly laid-out lines.
1918 // Updates the layout size with the previously laid-out lines.
1919 UpdateLayoutSize(lines,
1922 if(0u != newLines.Count())
1924 const LineRun& lastLine = *(newLines.End() - 1u);
1926 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1927 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1929 // Update the indices of the runs before the new laid-out lines are inserted.
1930 UpdateLineIndexOffsets(layoutParameters,
1935 // Insert the lines.
1936 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1943 lines.Resize(numberOfLines);
1946 // Rounds upward to avoid a non integer size.
1947 layoutSize.height = std::ceil(layoutSize.height);
1949 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1954 void Align(const Size& size,
1955 CharacterIndex startIndex,
1956 Length numberOfCharacters,
1957 Text::HorizontalAlignment::Type horizontalAlignment,
1958 Vector<LineRun>& lines,
1959 float& alignmentOffset,
1960 Dali::LayoutDirection::Type layoutDirection,
1961 bool matchLayoutDirection)
1963 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1965 alignmentOffset = MAX_FLOAT;
1966 // Traverse all lines and align the glyphs.
1967 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1971 LineRun& line = *it;
1973 if(line.characterRun.characterIndex < startIndex)
1975 // Do not align lines which have already been aligned.
1979 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1981 // Do not align lines beyond the last laid-out character.
1985 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1987 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1991 // Calculate the line's alignment offset accordingly with the align option,
1992 // the box width, line length, and the paragraph's direction.
1993 CalculateHorizontalAlignment(size.width,
1994 horizontalAlignment,
1997 matchLayoutDirection);
1999 // Updates the alignment offset.
2000 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2004 void CalculateHorizontalAlignment(float boxWidth,
2005 HorizontalAlignment::Type horizontalAlignment,
2007 Dali::LayoutDirection::Type layoutDirection,
2008 bool matchLayoutDirection)
2010 line.alignmentOffset = 0.f;
2011 const bool isLineRTL = RTL == line.direction;
2013 // Whether to swap the alignment.
2014 // 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.
2015 bool isLayoutRTL = isLineRTL;
2016 float lineLength = line.width;
2018 // match align for system language direction
2019 if(matchLayoutDirection)
2021 // Swap the alignment type if the line is right to left.
2022 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2024 // Calculate the horizontal line offset.
2025 switch(horizontalAlignment)
2027 case HorizontalAlignment::BEGIN:
2033 lineLength += line.extraLength;
2036 line.alignmentOffset = boxWidth - lineLength;
2040 line.alignmentOffset = 0.f;
2044 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2045 line.alignmentOffset -= line.extraLength;
2050 case HorizontalAlignment::CENTER:
2052 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2056 line.alignmentOffset -= line.extraLength;
2059 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2062 case HorizontalAlignment::END:
2066 line.alignmentOffset = 0.f;
2070 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2071 line.alignmentOffset -= line.extraLength;
2078 lineLength += line.extraLength;
2081 line.alignmentOffset = boxWidth - lineLength;
2088 void Initialize(LineRun& line)
2090 line.glyphRun.glyphIndex = 0u;
2091 line.glyphRun.numberOfGlyphs = 0u;
2092 line.characterRun.characterIndex = 0u;
2093 line.characterRun.numberOfCharacters = 0u;
2095 line.ascender = 0.f;
2096 line.descender = 0.f;
2097 line.extraLength = 0.f;
2098 line.alignmentOffset = 0.f;
2099 line.direction = LTR;
2100 line.ellipsis = false;
2101 line.lineSpacing = mDefaultLineSpacing;
2102 line.isSplitToTwoHalves = false;
2103 line.glyphRunSecondHalf.glyphIndex = 0u;
2104 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2105 line.characterRunForSecondHalfLine.characterIndex = 0u;
2106 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2111 float mDefaultLineSpacing;
2112 float mDefaultLineSize;
2114 IntrusivePtr<Metrics> mMetrics;
2115 float mRelativeLineSize;
2121 mImpl = new Engine::Impl();
2129 void Engine::SetMetrics(MetricsPtr& metrics)
2131 mImpl->mMetrics = metrics;
2134 void Engine::SetLayout(Type layout)
2136 mImpl->mLayout = layout;
2139 Engine::Type Engine::GetLayout() const
2141 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2142 return mImpl->mLayout;
2145 void Engine::SetCursorWidth(int width)
2147 mImpl->mCursorWidth = static_cast<float>(width);
2150 int Engine::GetCursorWidth() const
2152 return static_cast<int>(mImpl->mCursorWidth);
2155 bool Engine::LayoutText(Parameters& layoutParameters,
2157 bool elideTextEnabled,
2158 bool& isAutoScrollEnabled,
2159 DevelText::EllipsisPosition::Type ellipsisPosition)
2161 return mImpl->LayoutText(layoutParameters,
2164 isAutoScrollEnabled,
2168 void Engine::Align(const Size& size,
2169 CharacterIndex startIndex,
2170 Length numberOfCharacters,
2171 Text::HorizontalAlignment::Type horizontalAlignment,
2172 Vector<LineRun>& lines,
2173 float& alignmentOffset,
2174 Dali::LayoutDirection::Type layoutDirection,
2175 bool matchLayoutDirection)
2180 horizontalAlignment,
2184 matchLayoutDirection);
2187 void Engine::SetDefaultLineSpacing(float lineSpacing)
2189 mImpl->mDefaultLineSpacing = lineSpacing;
2192 float Engine::GetDefaultLineSpacing() const
2194 return mImpl->mDefaultLineSpacing;
2197 void Engine::SetDefaultLineSize(float lineSize)
2199 mImpl->mDefaultLineSize = lineSize;
2202 float Engine::GetDefaultLineSize() const
2204 return mImpl->mDefaultLineSize;
2207 void Engine::SetRelativeLineSize(float relativeLineSize)
2209 mImpl->mRelativeLineSize = relativeLineSize;
2212 float Engine::GetRelativeLineSize() const
2214 return mImpl->mRelativeLineSize;
2217 } // namespace Layout
2221 } // namespace Toolkit