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;
61 const float RELATIVE_LINE_SIZE = 1.f;
63 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
65 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
71 * @brief Stores temporary layout info of the line.
79 numberOfCharacters{0u},
86 whiteSpaceLengthEndOfLine{0.f},
88 isSplitToTwoHalves(false),
89 glyphIndexInSecondHalfLine{0u},
90 characterIndexInSecondHalfLine{0u},
91 numberOfGlyphsInSecondHalfLine{0u},
92 numberOfCharactersInSecondHalfLine{0u}
106 numberOfCharacters = 0u;
107 ascender = -MAX_FLOAT;
108 descender = MAX_FLOAT;
110 isSplitToTwoHalves = false;
111 glyphIndexInSecondHalfLine = 0u;
112 characterIndexInSecondHalfLine = 0u;
113 numberOfGlyphsInSecondHalfLine = 0u;
114 numberOfCharactersInSecondHalfLine = 0u;
117 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
118 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
119 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
120 Length numberOfCharacters; ///< The number of characters which fit in one line.
121 float ascender; ///< The maximum ascender of all fonts in the line.
122 float descender; ///< The minimum descender of all fonts in the line.
123 float lineSpacing; ///< The line spacing
124 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
125 float previousAdvance; ///< The advance of the previous glyph.
126 float length; ///< The current length of the line.
127 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
128 CharacterDirection direction;
130 bool isSplitToTwoHalves; ///< Whether the second half is defined.
131 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
132 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
133 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
134 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
137 struct LayoutBidiParameters
141 paragraphDirection = LTR;
142 bidiParagraphIndex = 0u;
144 isBidirectional = false;
147 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
148 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
149 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
150 bool isBidirectional = false; ///< Whether the text is bidirectional.
156 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
158 mDefaultLineSpacing{LINE_SPACING},
159 mDefaultLineSize{MIN_LINE_SIZE},
160 mRelativeLineSize{RELATIVE_LINE_SIZE}
165 * @brief get the line spacing.
167 * @param[in] textSize The text size.
168 * @return the line spacing value.
170 float GetLineSpacing(float textSize)
175 // Sets the line size
176 lineSpacing = mDefaultLineSize - textSize;
177 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
179 // Add the line spacing
180 lineSpacing += mDefaultLineSpacing;
182 //subtract line spcaing if relativeLineSize < 1 & larger than min height
183 relTextSize = textSize * mRelativeLineSize;
184 if(relTextSize > mDefaultLineSize)
186 if(mRelativeLineSize < 1)
188 //subtract the difference (always will be positive)
189 lineSpacing -= (textSize - relTextSize);
193 //reverse the addition in the top.
194 if(mDefaultLineSize > textSize)
196 lineSpacing -= mDefaultLineSize - textSize;
199 //add difference instead
200 lineSpacing += relTextSize - textSize;
208 * @brief Updates the line ascender and descender with the metrics of a new font.
210 * @param[in] glyphMetrics The metrics of the new font.
211 * @param[in,out] lineLayout The line layout.
213 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
215 Text::FontMetrics fontMetrics;
216 if(0u != glyphMetrics.fontId)
218 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
222 fontMetrics.ascender = glyphMetrics.fontHeight;
223 fontMetrics.descender = 0.f;
224 fontMetrics.height = fontMetrics.ascender;
225 fontMetrics.underlinePosition = 0.f;
226 fontMetrics.underlineThickness = 1.f;
229 // Sets the maximum ascender.
230 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
232 // Sets the minimum descender.
233 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
235 lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender);
239 * @brief Merges a temporary line layout into the line layout.
241 * @param[in,out] lineLayout The line layout.
242 * @param[in] tmpLineLayout A temporary line layout.
243 * @param[in] isShifted Whether to shift first glyph and character indices.
245 void MergeLineLayout(LineLayout& lineLayout,
246 const LineLayout& tmpLineLayout,
249 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
250 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
252 lineLayout.penX = tmpLineLayout.penX;
253 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
255 lineLayout.length = tmpLineLayout.length;
256 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
258 // Sets the maximum ascender.
259 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
261 // Sets the minimum descender.
262 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
264 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
267 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
268 lineLayout.characterIndex = tmpLineLayout.characterIndex;
271 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
272 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
273 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
274 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
275 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
278 void LayoutRightToLeft(const Parameters& parameters,
279 const BidirectionalLineInfoRun& bidirectionalLineInfo,
281 float& whiteSpaceLengthEndOfLine)
283 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
284 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
285 // then the first half of line "characterRun",
286 // Otherwise travers whole characters in"characterRun".
288 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
289 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
290 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
291 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
293 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
294 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
295 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
297 CharacterIndex characterLogicalIndex = 0u;
298 CharacterIndex characterVisualIndex = 0u;
300 float calculatedAdvance = 0.f;
302 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
303 // Otherwise maps the first visual index from visualToLogicalMap.
304 // This is to initialize the first visual index.
305 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
307 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
311 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
314 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
316 if(RTL == bidirectionalLineInfo.direction)
318 // If there are characters in the second half of Line.
319 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
321 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
322 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
324 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
326 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
327 whiteSpaceLengthEndOfLine += calculatedAdvance;
329 ++characterLogicalIndex;
330 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
334 // If all characters in the second half of Line are WhiteSpaces.
335 // then continue adding the WhiteSpaces from the first hel of Line.
336 // Also this is valid when the line was not splitted.
337 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
339 extendedToSecondHalf = true; // Whether the logical index is extended to second half
340 characterLogicalIndex = 0u;
341 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
343 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
344 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
346 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
348 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
349 whiteSpaceLengthEndOfLine += calculatedAdvance;
351 ++characterLogicalIndex;
352 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
357 // Here's the first index of character is not WhiteSpace
358 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
360 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
361 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
362 lastGlyphOfParagraphPlusOne,
363 charactersPerGlyphBuffer);
365 GlyphMetrics glyphMetrics;
366 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
367 GetGlyphsMetrics(glyphIndex,
368 numberOfGLyphsInGroup,
374 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
376 // Traverses the characters of the right to left paragraph.
377 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
378 if(!extendedToSecondHalf &&
379 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
381 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
383 // Convert the character in the logical order into the character in the visual order.
384 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
385 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
387 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
389 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
390 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
391 lastGlyphOfParagraphPlusOne,
392 charactersPerGlyphBuffer);
394 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
396 GlyphMetrics glyphMetrics;
397 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
398 GetGlyphsMetrics(glyphIndex,
399 numberOfGLyphsInGroup,
407 // If glyph is WhiteSpace then:
408 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
409 // the endOfLine in RTL was the headOfLine for layouting.
410 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
411 if(RTL == bidirectionalLineInfo.direction)
413 length += glyphMetrics.advance;
417 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
419 penX += glyphMetrics.advance;
423 // If glyph is not whiteSpace then:
424 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
425 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
426 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
427 // Otherwise the current length is maximum.
428 if(LTR == bidirectionalLineInfo.direction)
430 whiteSpaceLengthEndOfLine = 0.f;
432 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
433 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
438 // Continue traversing in the first half of line or in the whole line.
439 // If the second half of line was extended then continue from logical index in the first half of line
440 // Also this is valid when the line was not splitted and there were WhiteSpace.
441 // Otherwise start from first logical index in line.
442 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
443 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
445 // Convert the character in the logical order into the character in the visual order.
446 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
447 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
449 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
451 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
452 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
453 lastGlyphOfParagraphPlusOne,
454 charactersPerGlyphBuffer);
456 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
458 GlyphMetrics glyphMetrics;
459 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
460 GetGlyphsMetrics(glyphIndex,
461 numberOfGLyphsInGroup,
469 // If glyph is WhiteSpace then:
470 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
471 // the endOfLine in RTL was the headOfLine for layouting.
472 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
473 if(RTL == bidirectionalLineInfo.direction)
475 length += glyphMetrics.advance;
479 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
481 penX += glyphMetrics.advance;
485 // If glyph is not whiteSpace then:
486 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
487 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
488 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
489 // Otherwise the current length is maximum.
490 if(LTR == bidirectionalLineInfo.direction)
492 whiteSpaceLengthEndOfLine = 0.f;
494 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
495 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
500 void ReorderBiDiLayout(const Parameters& parameters,
501 LayoutBidiParameters& bidiParameters,
502 const LineLayout& currentLineLayout,
503 LineLayout& lineLayout,
504 bool breakInCharacters,
505 bool enforceEllipsisInSingleLine)
507 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
509 // The last glyph to be laid-out.
510 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
512 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
514 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
515 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
516 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
518 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
520 // Sets the visual to logical map tables needed to reorder the text.
521 ReorderLine(bidirectionalParagraphInfo,
522 bidirectionalLinesInfo,
523 bidiParameters.bidiLineIndex,
524 lineLayout.characterIndex,
525 lineLayout.numberOfCharacters,
526 lineLayout.characterIndexInSecondHalfLine,
527 lineLayout.numberOfCharactersInSecondHalfLine,
528 bidiParameters.paragraphDirection);
530 // Recalculate the length of the line and update the layout.
531 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
533 if(!bidirectionalLineInfo.isIdentity)
536 float whiteSpaceLengthEndOfLine = 0.f;
537 LayoutRightToLeft(parameters,
538 bidirectionalLineInfo,
540 whiteSpaceLengthEndOfLine);
542 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
543 if(!Equals(length, lineLayout.length))
545 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
547 if(isMultiline && (length > parameters.boundingBox.width))
549 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
551 // The word doesn't fit in one line. It has to be split by character.
553 // Remove the last laid out glyph(s) as they doesn't fit.
554 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
556 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
557 lastGlyphOfParagraphPlusOne,
558 charactersPerGlyphBuffer);
560 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
562 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
563 lineLayout.numberOfCharacters -= numberOfCharacters;
565 AdjustLayout(parameters,
567 bidirectionalParagraphInfo,
570 if(lineLayout.length < parameters.boundingBox.width)
575 if(glyphIndex < numberOfGLyphsInGroup)
577 // avoids go under zero for an unsigned int.
581 glyphIndex -= numberOfGLyphsInGroup;
586 lineLayout = currentLineLayout;
588 AdjustLayout(parameters,
590 bidirectionalParagraphInfo,
596 lineLayout.length = std::max(length, lineLayout.length);
603 void AdjustLayout(const Parameters& parameters,
604 LayoutBidiParameters& bidiParameters,
605 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
606 LineLayout& lineLayout)
608 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
610 // Remove current reordered line.
611 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
613 // Re-build the conversion table without the removed glyphs.
614 ReorderLine(bidirectionalParagraphInfo,
615 bidirectionalLinesInfo,
616 bidiParameters.bidiLineIndex,
617 lineLayout.characterIndex,
618 lineLayout.numberOfCharacters,
619 lineLayout.characterIndexInSecondHalfLine,
620 lineLayout.numberOfCharactersInSecondHalfLine,
621 bidiParameters.paragraphDirection);
623 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
626 float whiteSpaceLengthEndOfLine = 0.f;
627 LayoutRightToLeft(parameters,
628 bidirectionalLineInfo,
630 whiteSpaceLengthEndOfLine);
632 lineLayout.length = length;
633 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
637 * Retrieves the line layout for a given box width.
639 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
640 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
641 * to reorder the line and recalculate its length.
644 * @param[in] parameters The layout parameters.
645 * @param[] bidiParameters Bidirectional info for the current line.
646 * @param[out] lineLayout The line layout.
647 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
648 * @param[in] ellipsisPosition Where is the location the text elide
650 void GetLineLayoutForBox(const Parameters& parameters,
651 LayoutBidiParameters& bidiParameters,
652 LineLayout& lineLayout,
654 DevelText::EllipsisPosition::Type ellipsisPosition,
655 bool enforceEllipsisInSingleLine,
656 bool elideTextEnabled)
658 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
659 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
661 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
662 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
663 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
664 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
665 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
667 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
668 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
670 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
671 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
672 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
673 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
674 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
675 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
677 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
679 // The last glyph to be laid-out.
680 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
682 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
683 // In the case the line starts with a right to left character, if the width is longer than the advance,
684 // the difference needs to be added to the line length.
686 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
687 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
688 lastGlyphOfParagraphPlusOne,
689 charactersPerGlyphBuffer);
691 float targetWidth = parameters.boundingBox.width;
692 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
694 bool isSecondHalf = false;
696 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
697 float calculatedAdvance = 0.f;
698 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
699 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
701 GlyphMetrics glyphMetrics;
702 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
703 GetGlyphsMetrics(lineLayout.glyphIndex,
704 numberOfGLyphsInGroup,
710 // Set the direction of the first character of the line.
711 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
713 // Stores temporary line layout which has not been added to the final line layout.
714 LineLayout tmpLineLayout;
716 // Initialize the start point.
718 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
719 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
720 // 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.
721 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
723 // Calculate the line height if there is no characters.
724 FontId lastFontId = glyphMetrics.fontId;
725 UpdateLineHeight(glyphMetrics, tmpLineLayout);
727 bool oneWordLaidOut = false;
728 bool oneHyphenLaidOut = false;
729 GlyphIndex hyphenIndex = 0;
730 GlyphInfo hyphenGlyph;
732 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
733 glyphIndex < lastGlyphOfParagraphPlusOne;)
735 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
737 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
738 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
739 lastGlyphOfParagraphPlusOne,
740 charactersPerGlyphBuffer);
742 GlyphMetrics glyphMetrics;
743 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
744 GetGlyphsMetrics(glyphIndex,
745 numberOfGLyphsInGroup,
751 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
753 // Check if the font of the current glyph is the same of the previous one.
754 // If it's different the ascender and descender need to be updated.
755 if(lastFontId != glyphMetrics.fontId)
757 UpdateLineHeight(glyphMetrics, tmpLineLayout);
758 lastFontId = glyphMetrics.fontId;
761 // Get the character indices for the current glyph. The last character index is needed
762 // because there are glyphs formed by more than one character but their break info is
763 // given only for the last character.
764 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
765 const bool hasCharacters = charactersPerGlyph > 0u;
766 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
767 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
769 // Get the line break info for the current character.
770 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
774 // Increase the number of characters.
775 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
777 // Increase the number of glyphs.
778 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
782 // Increase the number of characters.
783 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
785 // Increase the number of glyphs.
786 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
789 // Check whether is a white space.
790 const Character character = *(textBuffer + characterFirstIndex);
791 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
793 // Calculate the length of the line.
795 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
796 const float previousTmpPenX = tmpLineLayout.penX;
797 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
798 const float previousTmpLength = tmpLineLayout.length;
799 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
803 // Add the length to the length of white spaces at the end of the line.
804 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
805 // The advance is used as the width is always zero for the white spaces.
809 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
810 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
812 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
814 // Clear the white space length at the end of the line.
815 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
818 if(isSplitToTwoHalves && (!isSecondHalf) &&
819 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
821 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
822 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
824 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
825 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
827 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
828 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
830 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
832 // Check if the accumulated length fits in the width of the box.
833 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
834 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
835 completelyFill && !isMultiline &&
836 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
838 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
840 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
842 GlyphMetrics glyphMetrics;
843 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
844 GetGlyphsMetrics(glyphIndexToRemove,
845 numberOfGLyphsInGroup,
851 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
852 lastGlyphOfParagraphPlusOne,
853 charactersPerGlyphBuffer);
855 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
856 const bool hasCharacters = charactersPerGlyph > 0u;
857 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
858 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
860 // Check whether is a white space.
861 const Character character = *(textBuffer + characterFirstIndex);
862 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
866 // Decrease the number of characters for SecondHalf.
867 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
869 // Decrease the number of glyphs for SecondHalf.
870 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
874 // Decrease the number of characters.
875 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
877 // Decrease the number of glyphs.
878 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
881 if(isRemovedGlyphWhiteSpace)
883 tmpLineLayout.penX -= glyphMetrics.advance;
884 tmpLineLayout.length -= glyphMetrics.advance;
888 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
889 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
894 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
895 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
896 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
900 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
901 tmpLineLayout.characterIndex = characterLastIndex + 1u;
902 glyphIndexToRemove = tmpLineLayout.glyphIndex;
906 else if((completelyFill || isMultiline) &&
907 (tmpLineLayout.length > targetWidth))
909 // Current word does not fit in the box's width.
910 if(((oneHyphenLaidOut && isHyphenMode) ||
911 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
914 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
915 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
918 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
920 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
922 // The word doesn't fit in the control's width. It needs to be split by character.
923 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
927 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
928 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
932 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
933 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
936 tmpLineLayout.penX = previousTmpPenX;
937 tmpLineLayout.previousAdvance = previousTmpAdvance;
938 tmpLineLayout.length = previousTmpLength;
939 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
942 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
944 // Add part of the word to the line layout and shift the first glyph.
945 MergeLineLayout(lineLayout, tmpLineLayout, true);
947 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
948 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
950 // Add part of the word to the line layout.
951 MergeLineLayout(lineLayout, tmpLineLayout, false);
956 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
959 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
961 // Reorder the RTL line.
962 if(bidiParameters.isBidirectional)
964 ReorderBiDiLayout(parameters,
969 enforceEllipsisInSingleLine);
975 if((isMultiline || isLastGlyph) &&
976 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
978 LineLayout currentLineLayout = lineLayout;
979 oneHyphenLaidOut = false;
981 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
983 // Must break the line. Update the line layout, shift the first glyph and return.
984 MergeLineLayout(lineLayout, tmpLineLayout, true);
988 // Must break the line. Update the line layout and return.
989 MergeLineLayout(lineLayout, tmpLineLayout, false);
992 // Reorder the RTL line.
993 if(bidiParameters.isBidirectional)
995 ReorderBiDiLayout(parameters,
1000 enforceEllipsisInSingleLine);
1003 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
1004 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1010 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1012 oneHyphenLaidOut = false;
1013 oneWordLaidOut = isWordLaidOut;
1014 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
1016 // Current glyph is the last one of the current word.
1017 // Add the temporal layout to the current one.
1018 MergeLineLayout(lineLayout, tmpLineLayout, false);
1020 tmpLineLayout.Clear();
1024 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1025 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1027 hyphenGlyph = GlyphInfo();
1028 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1030 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1031 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1033 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1035 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1037 hyphenIndex = glyphIndex;
1038 oneHyphenLaidOut = true;
1040 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1042 // Current glyph is the last one of the current word hyphen.
1043 // Add the temporal layout to the current one.
1044 MergeLineLayout(lineLayout, tmpLineLayout, false);
1046 tmpLineLayout.Clear();
1050 glyphIndex += numberOfGLyphsInGroup;
1053 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1056 void SetGlyphPositions(const Parameters& layoutParameters,
1057 Vector2* glyphPositionsBuffer,
1058 const LineLayout& layout)
1060 // Traverse the glyphs and set the positions.
1062 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1063 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1064 const Length numberOfGlyphs = layout.numberOfGlyphs;
1065 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1067 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1068 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1070 // Check if the x bearing of the first character is negative.
1071 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1072 // so the penX position needs to be moved to the right.
1073 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1074 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1076 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1077 layoutParameters.textModel->mLogicalModel,
1078 interGlyphExtraAdvance,
1080 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1081 startIndexForGlyphPositions,
1082 glyphPositionsBuffer,
1085 if(layout.isSplitToTwoHalves)
1087 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1088 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1089 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1091 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1092 layoutParameters.textModel->mLogicalModel,
1093 interGlyphExtraAdvance,
1094 numberOfGlyphsInSecondHalfLine,
1095 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1096 startIndexForGlyphPositionsnSecondHalf,
1097 glyphPositionsBuffer,
1102 void SetGlyphPositions(const Parameters& layoutParameters,
1103 Vector2* glyphPositionsBuffer,
1104 LayoutBidiParameters& layoutBidiParameters,
1105 const LineLayout& layout)
1107 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1108 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1109 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1111 CharacterIndex characterLogicalIndex = 0u;
1112 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1113 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1117 if(layout.isSplitToTwoHalves)
1119 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1120 layoutParameters.textModel->mLogicalModel,
1121 layoutBidiParameters.bidiLineIndex,
1122 layoutParameters.startGlyphIndex,
1123 glyphPositionsBuffer,
1124 characterVisualIndex,
1125 characterLogicalIndex,
1129 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1131 extendedToSecondHalf = true;
1132 characterLogicalIndex = 0u;
1133 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1135 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1136 layoutParameters.textModel->mLogicalModel,
1137 layoutBidiParameters.bidiLineIndex,
1138 layoutParameters.startGlyphIndex,
1139 glyphPositionsBuffer,
1140 characterVisualIndex,
1141 characterLogicalIndex,
1145 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1146 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1148 penX += -glyph.xBearing;
1150 // Traverses the characters of the right to left paragraph.
1151 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1153 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1154 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1155 layoutParameters.startGlyphIndex,
1156 layoutParameters.interGlyphExtraAdvance,
1157 bidiLine.characterRunForSecondHalfLine,
1158 bidiLine.visualToLogicalMapSecondHalf,
1159 glyphPositionsBuffer,
1160 characterLogicalIndex,
1164 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1166 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1167 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1168 layoutParameters.startGlyphIndex,
1169 layoutParameters.interGlyphExtraAdvance,
1170 bidiLine.characterRun,
1171 bidiLine.visualToLogicalMap,
1172 glyphPositionsBuffer,
1173 characterLogicalIndex,
1178 * @brief Resizes the line buffer.
1180 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1181 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1182 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1183 * @param[in] updateCurrentBuffer Whether the layout is updated.
1185 * @return Pointer to either lines or newLines.
1187 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1188 Vector<LineRun>& newLines,
1189 Length& linesCapacity,
1190 bool updateCurrentBuffer)
1192 LineRun* linesBuffer = nullptr;
1193 // Reserve more space for the next lines.
1194 linesCapacity *= 2u;
1195 if(updateCurrentBuffer)
1197 newLines.Resize(linesCapacity);
1198 linesBuffer = newLines.Begin();
1202 lines.Resize(linesCapacity);
1203 linesBuffer = lines.Begin();
1210 * Ellipsis a line if it exceeds the width's of the bounding box.
1212 * @param[in] layoutParameters The parameters needed to layout the text.
1213 * @param[in] layout The line layout.
1214 * @param[in,out] layoutSize The text's layout size.
1215 * @param[in,out] linesBuffer Pointer to the line's buffer.
1216 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1217 * @param[in,out] numberOfLines The number of laid-out lines.
1218 * @param[in] penY The vertical layout position.
1219 * @param[in] currentParagraphDirection The current paragraph's direction.
1220 * @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
1221 * @param[in] ellipsisPosition Where is the location the text elide
1223 * return Whether the line is ellipsized.
1225 bool EllipsisLine(const Parameters& layoutParameters,
1226 LayoutBidiParameters& layoutBidiParameters,
1227 const LineLayout& layout,
1229 LineRun* linesBuffer,
1230 Vector2* glyphPositionsBuffer,
1231 Length& numberOfLines,
1233 bool& isAutoScrollEnabled,
1234 DevelText::EllipsisPosition::Type ellipsisPosition,
1235 bool enforceEllipsisInSingleLine)
1237 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))));
1238 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1239 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1241 isAutoScrollEnabled = false;
1242 // Do not layout more lines if ellipsis is enabled.
1244 // The last line needs to be completely filled with characters.
1245 // Part of a word may be used.
1247 LineRun* lineRun = nullptr;
1248 LineLayout ellipsisLayout;
1249 if(0u != numberOfLines)
1251 // Get the last line and layout it again with the 'completelyFill' flag to true.
1252 lineRun = linesBuffer + (numberOfLines - 1u);
1253 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1255 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1259 // At least there is space reserved for one line.
1260 lineRun = linesBuffer;
1262 lineRun->glyphRun.glyphIndex = 0u;
1263 ellipsisLayout.glyphIndex = 0u;
1264 lineRun->isSplitToTwoHalves = false;
1269 GetLineLayoutForBox(layoutParameters,
1270 layoutBidiParameters,
1274 enforceEllipsisInSingleLine,
1277 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1279 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1282 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1283 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1284 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1285 lineRun->width = ellipsisLayout.length;
1286 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1287 lineRun->ascender = ellipsisLayout.ascender;
1288 lineRun->descender = ellipsisLayout.descender;
1289 lineRun->ellipsis = true;
1291 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1292 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1293 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1294 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1295 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1297 layoutSize.width = layoutParameters.boundingBox.width;
1298 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1300 layoutSize.height += GetLineHeight(*lineRun);
1303 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1305 if(layoutBidiParameters.isBidirectional)
1307 layoutBidiParameters.bidiLineIndex = 0u;
1308 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1309 endIt = bidirectionalLinesInfo.End();
1311 ++it, ++layoutBidiParameters.bidiLineIndex)
1313 const BidirectionalLineInfoRun& run = *it;
1314 //To handle case when the laid characters exist in next line.
1315 //More than one BidirectionalLineInfoRun could start with same character.
1316 //When need to check also numberOfCharacters in line.
1317 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1318 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1319 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1320 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1321 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1323 // Found where to insert the bidi line info.
1329 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1331 if((nullptr != bidirectionalLineInfo) &&
1332 !bidirectionalLineInfo->isIdentity &&
1333 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1335 lineRun->direction = RTL;
1336 SetGlyphPositions(layoutParameters,
1337 glyphPositionsBuffer,
1338 layoutBidiParameters,
1343 lineRun->direction = LTR;
1344 SetGlyphPositions(layoutParameters,
1345 glyphPositionsBuffer,
1354 * @brief Updates the text layout with a new laid-out line.
1356 * @param[in] layoutParameters The parameters needed to layout the text.
1357 * @param[in] layout The line layout.
1358 * @param[in,out] layoutSize The text's layout size.
1359 * @param[in,out] linesBuffer Pointer to the line's buffer.
1360 * @param[in] index Index to the vector of glyphs.
1361 * @param[in,out] numberOfLines The number of laid-out lines.
1362 * @param[in] isLastLine Whether the laid-out line is the last one.
1364 void UpdateTextLayout(const Parameters& layoutParameters,
1365 const LineLayout& layout,
1367 LineRun* linesBuffer,
1369 Length& numberOfLines,
1372 LineRun& lineRun = *(linesBuffer + numberOfLines);
1375 lineRun.glyphRun.glyphIndex = index;
1376 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1377 lineRun.characterRun.characterIndex = layout.characterIndex;
1378 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1379 lineRun.width = layout.length;
1380 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1382 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1383 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1384 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1385 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1386 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1388 // Rounds upward to avoid a non integer size.
1389 lineRun.width = std::ceil(lineRun.width);
1391 lineRun.ascender = layout.ascender;
1392 lineRun.descender = layout.descender;
1393 lineRun.direction = layout.direction;
1394 lineRun.ellipsis = false;
1396 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1398 // Update the actual size.
1399 if(lineRun.width > layoutSize.width)
1401 layoutSize.width = lineRun.width;
1404 layoutSize.height += GetLineHeight(lineRun);
1408 * @brief Updates the text layout with the last laid-out line.
1410 * @param[in] layoutParameters The parameters needed to layout the text.
1411 * @param[in] characterIndex The character index of the line.
1412 * @param[in] glyphIndex The glyph index of the line.
1413 * @param[in,out] layoutSize The text's layout size.
1414 * @param[in,out] linesBuffer Pointer to the line's buffer.
1415 * @param[in,out] numberOfLines The number of laid-out lines.
1417 void UpdateTextLayout(const Parameters& layoutParameters,
1418 CharacterIndex characterIndex,
1419 GlyphIndex glyphIndex,
1421 LineRun* linesBuffer,
1422 Length& numberOfLines)
1424 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1426 // Need to add a new line with no characters but with height to increase the layoutSize.height
1427 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1429 Text::FontMetrics fontMetrics;
1430 if(0u != glyphInfo.fontId)
1432 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1435 LineRun& lineRun = *(linesBuffer + numberOfLines);
1438 lineRun.glyphRun.glyphIndex = glyphIndex;
1439 lineRun.glyphRun.numberOfGlyphs = 0u;
1440 lineRun.characterRun.characterIndex = characterIndex;
1441 lineRun.characterRun.numberOfCharacters = 0u;
1442 lineRun.width = 0.f;
1443 lineRun.ascender = fontMetrics.ascender;
1444 lineRun.descender = fontMetrics.descender;
1445 lineRun.extraLength = 0.f;
1446 lineRun.alignmentOffset = 0.f;
1447 lineRun.direction = LTR;
1448 lineRun.ellipsis = false;
1450 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1452 layoutSize.height += GetLineHeight(lineRun);
1456 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1458 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1459 * @param[in,out] layoutSize The text's layout size.
1461 void UpdateLayoutSize(const Vector<LineRun>& lines,
1464 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1465 endIt = lines.End();
1469 const LineRun& line = *it;
1471 if(line.width > layoutSize.width)
1473 layoutSize.width = line.width;
1476 layoutSize.height += GetLineHeight(line);
1481 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1483 * @param[in] layoutParameters The parameters needed to layout the text.
1484 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1485 * @param[in] characterOffset The offset to be added to the runs of characters.
1486 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1488 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1489 Vector<LineRun>& lines,
1490 Length characterOffset,
1493 // Update the glyph and character runs.
1494 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1495 endIt = lines.End();
1499 LineRun& line = *it;
1501 line.glyphRun.glyphIndex = glyphOffset;
1502 line.characterRun.characterIndex = characterOffset;
1504 glyphOffset += line.glyphRun.numberOfGlyphs;
1505 characterOffset += line.characterRun.numberOfCharacters;
1509 bool LayoutText(Parameters& layoutParameters,
1511 bool elideTextEnabled,
1512 bool& isAutoScrollEnabled,
1513 DevelText::EllipsisPosition::Type ellipsisPosition)
1515 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1516 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1518 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1519 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1521 //Reset indices of ElidedGlyphs
1522 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1523 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1524 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1525 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1527 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1529 if(0u == layoutParameters.numberOfGlyphs)
1531 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1532 if(layoutParameters.isLastNewParagraph)
1534 Length numberOfLines = lines.Count();
1535 if(0u != numberOfLines)
1537 const LineRun& lastLine = *(lines.End() - 1u);
1539 if(0u != lastLine.characterRun.numberOfCharacters)
1541 // Need to add a new line with no characters but with height to increase the layoutSize.height
1543 Initialize(newLine);
1544 lines.PushBack(newLine);
1546 UpdateTextLayout(layoutParameters,
1547 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1548 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1556 // Calculates the layout size.
1557 UpdateLayoutSize(lines,
1560 // Rounds upward to avoid a non integer size.
1561 layoutSize.height = std::ceil(layoutSize.height);
1563 // Nothing else do if there are no glyphs to layout.
1567 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1568 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1569 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1571 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1572 // This extra line needs to be removed.
1573 if(0u != lines.Count())
1575 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1577 if((0u == lastLine->characterRun.numberOfCharacters) &&
1578 (lastGlyphPlusOne == totalNumberOfGlyphs))
1580 lines.Remove(lastLine);
1584 // Retrieve BiDi info.
1585 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1587 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1588 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1589 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1591 // Set the layout bidirectional paramters.
1592 LayoutBidiParameters layoutBidiParameters;
1594 // Whether the layout is being updated or set from scratch.
1595 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1597 Vector2* glyphPositionsBuffer = nullptr;
1598 Vector<Vector2> newGlyphPositions;
1600 LineRun* linesBuffer = nullptr;
1601 Vector<LineRun> newLines;
1603 // Estimate the number of lines.
1604 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1605 Length numberOfLines = 0u;
1607 if(updateCurrentBuffer)
1609 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1610 glyphPositionsBuffer = newGlyphPositions.Begin();
1612 newLines.Resize(linesCapacity);
1613 linesBuffer = newLines.Begin();
1617 glyphPositionsBuffer = glyphPositions.Begin();
1619 lines.Resize(linesCapacity);
1620 linesBuffer = lines.Begin();
1623 float penY = CalculateLineOffset(lines,
1624 layoutParameters.startLineIndex);
1625 bool anyLineIsEliped = false;
1626 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1628 layoutBidiParameters.Clear();
1630 if(hasBidiParagraphs)
1632 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1634 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1635 endIt = bidirectionalParagraphsInfo.End();
1637 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1639 const BidirectionalParagraphInfoRun& run = *it;
1641 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1643 if(lastCharacterIndex <= startCharacterIndex)
1645 // Do not process, the paragraph has already been processed.
1649 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1651 layoutBidiParameters.paragraphDirection = run.direction;
1652 layoutBidiParameters.isBidirectional = true;
1655 // Has already been found.
1659 if(layoutBidiParameters.isBidirectional)
1661 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1662 endIt = bidirectionalLinesInfo.End();
1664 ++it, ++layoutBidiParameters.bidiLineIndex)
1666 const BidirectionalLineInfoRun& run = *it;
1668 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1670 if(lastCharacterIndex <= startCharacterIndex)
1676 if(startCharacterIndex < lastCharacterIndex)
1678 // Found where to insert the bidi line info.
1685 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1687 // Get the layout for the line.
1689 layout.direction = layoutBidiParameters.paragraphDirection;
1690 layout.glyphIndex = index;
1691 GetLineLayoutForBox(layoutParameters,
1692 layoutBidiParameters,
1699 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1700 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1701 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1702 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1703 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1705 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1707 // The width is too small and no characters are laid-out.
1708 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1710 lines.Resize(numberOfLines);
1712 // Rounds upward to avoid a non integer size.
1713 layoutSize.height = std::ceil(layoutSize.height);
1718 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1720 penY += layout.ascender;
1722 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1724 bool ellipsis = false;
1725 if(elideTextEnabled)
1727 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1729 // Does the ellipsis of the last line.
1730 ellipsis = EllipsisLine(layoutParameters,
1731 layoutBidiParameters,
1735 glyphPositionsBuffer,
1738 isAutoScrollEnabled,
1743 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1745 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1746 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1748 ellipsis = EllipsisLine(layoutParameters,
1749 layoutBidiParameters,
1753 glyphPositionsBuffer,
1756 isAutoScrollEnabled,
1761 //clear hyphen from ellipsis line
1762 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1763 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1765 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1767 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1768 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1772 // No more lines to layout.
1777 //In START location of ellipsis whether to shift lines or not.
1778 anyLineIsEliped |= ellipsis;
1780 // Whether the last line has been laid-out.
1781 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1783 if(numberOfLines == linesCapacity)
1785 // Reserve more space for the next lines.
1786 linesBuffer = ResizeLinesBuffer(lines,
1789 updateCurrentBuffer);
1792 // Updates the current text's layout with the line's layout.
1793 UpdateTextLayout(layoutParameters,
1801 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1803 if((nextIndex == totalNumberOfGlyphs) &&
1804 layoutParameters.isLastNewParagraph &&
1805 (mLayout == MULTI_LINE_BOX))
1807 // The last character of the text is a new paragraph character.
1808 // An extra line with no characters is added to increase the text's height
1809 // in order to place the cursor.
1811 if(numberOfLines == linesCapacity)
1813 // Reserve more space for the next lines.
1814 linesBuffer = ResizeLinesBuffer(lines,
1817 updateCurrentBuffer);
1820 UpdateTextLayout(layoutParameters,
1821 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1822 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1826 } // whether to add a last line.
1828 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1830 if((nullptr != bidirectionalLineInfo) &&
1831 !bidirectionalLineInfo->isIdentity &&
1832 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1834 SetGlyphPositions(layoutParameters,
1835 glyphPositionsBuffer,
1836 layoutBidiParameters,
1841 // Sets the positions of the glyphs.
1842 SetGlyphPositions(layoutParameters,
1843 glyphPositionsBuffer,
1847 // Updates the vertical pen's position.
1848 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender);
1850 // Increase the glyph index.
1853 } // end for() traversing glyphs.
1855 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1856 if(anyLineIsEliped && numberOfLines > 1u)
1858 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1860 Length lineIndex = 0;
1861 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1863 LineRun& delLine = linesBuffer[lineIndex];
1864 delLine.ellipsis = true;
1866 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1867 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1869 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1870 linesBuffer[lineIndex].ellipsis = false;
1874 linesBuffer[0u].ellipsis = true;
1876 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1878 Length middleLineIndex = (numberOfLines) / 2u;
1879 Length ellipsisLineIndex = 0u;
1880 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1882 LineRun& delLine = linesBuffer[middleLineIndex];
1883 delLine.ellipsis = true;
1885 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1886 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1888 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1889 linesBuffer[lineIndex].ellipsis = false;
1892 ellipsisLineIndex = middleLineIndex - 1u;
1893 middleLineIndex = (numberOfLines) / 2u;
1896 linesBuffer[ellipsisLineIndex].ellipsis = true;
1900 if(updateCurrentBuffer)
1902 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1903 newGlyphPositions.Begin(),
1904 newGlyphPositions.End());
1905 glyphPositions.Resize(totalNumberOfGlyphs);
1907 newLines.Resize(numberOfLines);
1909 // Current text's layout size adds only the newly laid-out lines.
1910 // Updates the layout size with the previously laid-out lines.
1911 UpdateLayoutSize(lines,
1914 if(0u != newLines.Count())
1916 const LineRun& lastLine = *(newLines.End() - 1u);
1918 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1919 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1921 // Update the indices of the runs before the new laid-out lines are inserted.
1922 UpdateLineIndexOffsets(layoutParameters,
1927 // Insert the lines.
1928 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1935 lines.Resize(numberOfLines);
1938 // Rounds upward to avoid a non integer size.
1939 layoutSize.height = std::ceil(layoutSize.height);
1941 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1946 void Align(const Size& size,
1947 CharacterIndex startIndex,
1948 Length numberOfCharacters,
1949 Text::HorizontalAlignment::Type horizontalAlignment,
1950 Vector<LineRun>& lines,
1951 float& alignmentOffset,
1952 Dali::LayoutDirection::Type layoutDirection,
1953 bool matchLayoutDirection)
1955 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1957 alignmentOffset = MAX_FLOAT;
1958 // Traverse all lines and align the glyphs.
1959 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1963 LineRun& line = *it;
1965 if(line.characterRun.characterIndex < startIndex)
1967 // Do not align lines which have already been aligned.
1971 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1973 // Do not align lines beyond the last laid-out character.
1977 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1979 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1983 // Calculate the line's alignment offset accordingly with the align option,
1984 // the box width, line length, and the paragraph's direction.
1985 CalculateHorizontalAlignment(size.width,
1986 horizontalAlignment,
1989 matchLayoutDirection);
1991 // Updates the alignment offset.
1992 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
1996 void CalculateHorizontalAlignment(float boxWidth,
1997 HorizontalAlignment::Type horizontalAlignment,
1999 Dali::LayoutDirection::Type layoutDirection,
2000 bool matchLayoutDirection)
2002 line.alignmentOffset = 0.f;
2003 const bool isLineRTL = RTL == line.direction;
2005 // Whether to swap the alignment.
2006 // 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.
2007 bool isLayoutRTL = isLineRTL;
2008 float lineLength = line.width;
2010 // match align for system language direction
2011 if(matchLayoutDirection)
2013 // Swap the alignment type if the line is right to left.
2014 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2016 // Calculate the horizontal line offset.
2017 switch(horizontalAlignment)
2019 case HorizontalAlignment::BEGIN:
2025 lineLength += line.extraLength;
2028 line.alignmentOffset = boxWidth - lineLength;
2032 line.alignmentOffset = 0.f;
2036 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2037 line.alignmentOffset -= line.extraLength;
2042 case HorizontalAlignment::CENTER:
2044 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2048 line.alignmentOffset -= line.extraLength;
2051 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2054 case HorizontalAlignment::END:
2058 line.alignmentOffset = 0.f;
2062 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2063 line.alignmentOffset -= line.extraLength;
2070 lineLength += line.extraLength;
2073 line.alignmentOffset = boxWidth - lineLength;
2080 void Initialize(LineRun& line)
2082 line.glyphRun.glyphIndex = 0u;
2083 line.glyphRun.numberOfGlyphs = 0u;
2084 line.characterRun.characterIndex = 0u;
2085 line.characterRun.numberOfCharacters = 0u;
2087 line.ascender = 0.f;
2088 line.descender = 0.f;
2089 line.extraLength = 0.f;
2090 line.alignmentOffset = 0.f;
2091 line.direction = LTR;
2092 line.ellipsis = false;
2093 line.lineSpacing = mDefaultLineSpacing;
2094 line.isSplitToTwoHalves = false;
2095 line.glyphRunSecondHalf.glyphIndex = 0u;
2096 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2097 line.characterRunForSecondHalfLine.characterIndex = 0u;
2098 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2103 float mDefaultLineSpacing;
2104 float mDefaultLineSize;
2106 IntrusivePtr<Metrics> mMetrics;
2107 float mRelativeLineSize;
2113 mImpl = new Engine::Impl();
2121 void Engine::SetMetrics(MetricsPtr& metrics)
2123 mImpl->mMetrics = metrics;
2126 void Engine::SetLayout(Type layout)
2128 mImpl->mLayout = layout;
2131 Engine::Type Engine::GetLayout() const
2133 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2134 return mImpl->mLayout;
2137 void Engine::SetCursorWidth(int width)
2139 mImpl->mCursorWidth = static_cast<float>(width);
2142 int Engine::GetCursorWidth() const
2144 return static_cast<int>(mImpl->mCursorWidth);
2147 bool Engine::LayoutText(Parameters& layoutParameters,
2149 bool elideTextEnabled,
2150 bool& isAutoScrollEnabled,
2151 DevelText::EllipsisPosition::Type ellipsisPosition)
2153 return mImpl->LayoutText(layoutParameters,
2156 isAutoScrollEnabled,
2160 void Engine::Align(const Size& size,
2161 CharacterIndex startIndex,
2162 Length numberOfCharacters,
2163 Text::HorizontalAlignment::Type horizontalAlignment,
2164 Vector<LineRun>& lines,
2165 float& alignmentOffset,
2166 Dali::LayoutDirection::Type layoutDirection,
2167 bool matchLayoutDirection)
2172 horizontalAlignment,
2176 matchLayoutDirection);
2179 void Engine::SetDefaultLineSpacing(float lineSpacing)
2181 mImpl->mDefaultLineSpacing = lineSpacing;
2184 float Engine::GetDefaultLineSpacing() const
2186 return mImpl->mDefaultLineSpacing;
2189 void Engine::SetDefaultLineSize(float lineSize)
2191 mImpl->mDefaultLineSize = lineSize;
2194 float Engine::GetDefaultLineSize() const
2196 return mImpl->mDefaultLineSize;
2199 void Engine::SetRelativeLineSize(float relativeLineSize)
2201 mImpl->mRelativeLineSize = relativeLineSize;
2204 float Engine::GetRelativeLineSize() const
2206 return mImpl->mRelativeLineSize;
2209 } // namespace Layout
2213 } // namespace Toolkit