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);
1311 //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1312 //usually we don't includ it in normal cases using GetLineHeight()
1313 if(lineRun->lineSpacing < 0)
1315 layoutSize.height -= lineRun->lineSpacing;
1319 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1321 if(layoutBidiParameters.isBidirectional)
1323 layoutBidiParameters.bidiLineIndex = 0u;
1324 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1325 endIt = bidirectionalLinesInfo.End();
1327 ++it, ++layoutBidiParameters.bidiLineIndex)
1329 const BidirectionalLineInfoRun& run = *it;
1330 //To handle case when the laid characters exist in next line.
1331 //More than one BidirectionalLineInfoRun could start with same character.
1332 //When need to check also numberOfCharacters in line.
1333 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1334 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1335 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1336 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1337 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1339 // Found where to insert the bidi line info.
1345 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1347 if((nullptr != bidirectionalLineInfo) &&
1348 !bidirectionalLineInfo->isIdentity &&
1349 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1351 lineRun->direction = RTL;
1352 SetGlyphPositions(layoutParameters,
1353 glyphPositionsBuffer,
1354 layoutBidiParameters,
1359 lineRun->direction = LTR;
1360 SetGlyphPositions(layoutParameters,
1361 glyphPositionsBuffer,
1370 * @brief Updates the text layout with a new laid-out line.
1372 * @param[in] layoutParameters The parameters needed to layout the text.
1373 * @param[in] layout The line layout.
1374 * @param[in,out] layoutSize The text's layout size.
1375 * @param[in,out] linesBuffer Pointer to the line's buffer.
1376 * @param[in] index Index to the vector of glyphs.
1377 * @param[in,out] numberOfLines The number of laid-out lines.
1378 * @param[in] isLastLine Whether the laid-out line is the last one.
1380 void UpdateTextLayout(const Parameters& layoutParameters,
1381 const LineLayout& layout,
1383 LineRun* linesBuffer,
1385 Length& numberOfLines,
1388 LineRun& lineRun = *(linesBuffer + numberOfLines);
1391 lineRun.glyphRun.glyphIndex = index;
1392 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1393 lineRun.characterRun.characterIndex = layout.characterIndex;
1394 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1395 lineRun.width = layout.length;
1396 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1398 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1399 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1400 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1401 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1402 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1404 // Rounds upward to avoid a non integer size.
1405 lineRun.width = std::ceil(lineRun.width);
1407 lineRun.ascender = layout.ascender;
1408 lineRun.descender = layout.descender;
1409 lineRun.direction = layout.direction;
1410 lineRun.ellipsis = false;
1412 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1414 // Update the actual size.
1415 if(lineRun.width > layoutSize.width)
1417 layoutSize.width = lineRun.width;
1420 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1424 * @brief Updates the text layout with the last laid-out line.
1426 * @param[in] layoutParameters The parameters needed to layout the text.
1427 * @param[in] characterIndex The character index of the line.
1428 * @param[in] glyphIndex The glyph index of the line.
1429 * @param[in,out] layoutSize The text's layout size.
1430 * @param[in,out] linesBuffer Pointer to the line's buffer.
1431 * @param[in,out] numberOfLines The number of laid-out lines.
1433 void UpdateTextLayout(const Parameters& layoutParameters,
1434 CharacterIndex characterIndex,
1435 GlyphIndex glyphIndex,
1437 LineRun* linesBuffer,
1438 Length& numberOfLines)
1440 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1442 // Need to add a new line with no characters but with height to increase the layoutSize.height
1443 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1445 Text::FontMetrics fontMetrics;
1446 if(0u != glyphInfo.fontId)
1448 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1451 LineRun& lineRun = *(linesBuffer + numberOfLines);
1454 lineRun.glyphRun.glyphIndex = glyphIndex;
1455 lineRun.glyphRun.numberOfGlyphs = 0u;
1456 lineRun.characterRun.characterIndex = characterIndex;
1457 lineRun.characterRun.numberOfCharacters = 0u;
1458 lineRun.width = 0.f;
1459 lineRun.ascender = fontMetrics.ascender;
1460 lineRun.descender = fontMetrics.descender;
1461 lineRun.extraLength = 0.f;
1462 lineRun.alignmentOffset = 0.f;
1463 lineRun.direction = LTR;
1464 lineRun.ellipsis = false;
1466 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1468 layoutSize.height += GetLineHeight(lineRun, true);
1472 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1474 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1475 * @param[in,out] layoutSize The text's layout size.
1477 void UpdateLayoutSize(const Vector<LineRun>& lines,
1480 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1481 endIt = lines.End();
1485 const LineRun& line = *it;
1486 bool isLastLine = (it + 1 == endIt);
1488 if(line.width > layoutSize.width)
1490 layoutSize.width = line.width;
1493 layoutSize.height += GetLineHeight(line, isLastLine);
1498 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1500 * @param[in] layoutParameters The parameters needed to layout the text.
1501 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1502 * @param[in] characterOffset The offset to be added to the runs of characters.
1503 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1505 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1506 Vector<LineRun>& lines,
1507 Length characterOffset,
1510 // Update the glyph and character runs.
1511 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1512 endIt = lines.End();
1516 LineRun& line = *it;
1518 line.glyphRun.glyphIndex = glyphOffset;
1519 line.characterRun.characterIndex = characterOffset;
1521 glyphOffset += line.glyphRun.numberOfGlyphs;
1522 characterOffset += line.characterRun.numberOfCharacters;
1526 bool LayoutText(Parameters& layoutParameters,
1528 bool elideTextEnabled,
1529 bool& isAutoScrollEnabled,
1530 DevelText::EllipsisPosition::Type ellipsisPosition)
1532 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1533 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1535 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1536 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1538 //Reset indices of ElidedGlyphs
1539 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1540 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1541 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1542 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1544 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1546 if(0u == layoutParameters.numberOfGlyphs)
1548 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1549 if(layoutParameters.isLastNewParagraph)
1551 Length numberOfLines = lines.Count();
1552 if(0u != numberOfLines)
1554 const LineRun& lastLine = *(lines.End() - 1u);
1556 if(0u != lastLine.characterRun.numberOfCharacters)
1558 // Need to add a new line with no characters but with height to increase the layoutSize.height
1560 Initialize(newLine);
1561 lines.PushBack(newLine);
1563 UpdateTextLayout(layoutParameters,
1564 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1565 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1573 // Calculates the layout size.
1574 UpdateLayoutSize(lines,
1577 // Rounds upward to avoid a non integer size.
1578 layoutSize.height = std::ceil(layoutSize.height);
1580 // Nothing else do if there are no glyphs to layout.
1584 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1585 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1586 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1588 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1589 // This extra line needs to be removed.
1590 if(0u != lines.Count())
1592 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1594 if((0u == lastLine->characterRun.numberOfCharacters) &&
1595 (lastGlyphPlusOne == totalNumberOfGlyphs))
1597 lines.Remove(lastLine);
1601 // Retrieve BiDi info.
1602 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1604 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1605 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1606 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1608 // Set the layout bidirectional paramters.
1609 LayoutBidiParameters layoutBidiParameters;
1611 // Whether the layout is being updated or set from scratch.
1612 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1614 Vector2* glyphPositionsBuffer = nullptr;
1615 Vector<Vector2> newGlyphPositions;
1617 LineRun* linesBuffer = nullptr;
1618 Vector<LineRun> newLines;
1620 // Estimate the number of lines.
1621 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1622 Length numberOfLines = 0u;
1624 if(updateCurrentBuffer)
1626 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1627 glyphPositionsBuffer = newGlyphPositions.Begin();
1629 newLines.Resize(linesCapacity);
1630 linesBuffer = newLines.Begin();
1634 glyphPositionsBuffer = glyphPositions.Begin();
1636 lines.Resize(linesCapacity);
1637 linesBuffer = lines.Begin();
1640 float penY = CalculateLineOffset(lines,
1641 layoutParameters.startLineIndex);
1642 bool anyLineIsEliped = false;
1643 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1645 layoutBidiParameters.Clear();
1647 if(hasBidiParagraphs)
1649 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1651 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1652 endIt = bidirectionalParagraphsInfo.End();
1654 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1656 const BidirectionalParagraphInfoRun& run = *it;
1658 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1660 if(lastCharacterIndex <= startCharacterIndex)
1662 // Do not process, the paragraph has already been processed.
1666 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1668 layoutBidiParameters.paragraphDirection = run.direction;
1669 layoutBidiParameters.isBidirectional = true;
1672 // Has already been found.
1676 if(layoutBidiParameters.isBidirectional)
1678 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1679 endIt = bidirectionalLinesInfo.End();
1681 ++it, ++layoutBidiParameters.bidiLineIndex)
1683 const BidirectionalLineInfoRun& run = *it;
1685 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1687 if(lastCharacterIndex <= startCharacterIndex)
1693 if(startCharacterIndex < lastCharacterIndex)
1695 // Found where to insert the bidi line info.
1702 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1704 // Get the layout for the line.
1706 layout.direction = layoutBidiParameters.paragraphDirection;
1707 layout.glyphIndex = index;
1708 GetLineLayoutForBox(layoutParameters,
1709 layoutBidiParameters,
1716 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1717 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1718 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1719 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1720 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1722 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1724 // The width is too small and no characters are laid-out.
1725 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1727 lines.Resize(numberOfLines);
1729 // Rounds upward to avoid a non integer size.
1730 layoutSize.height = std::ceil(layoutSize.height);
1735 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1737 penY += layout.ascender;
1739 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1741 bool ellipsis = false;
1742 if(elideTextEnabled)
1744 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1746 // Does the ellipsis of the last line.
1747 ellipsis = EllipsisLine(layoutParameters,
1748 layoutBidiParameters,
1752 glyphPositionsBuffer,
1755 isAutoScrollEnabled,
1760 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1762 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1763 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1765 ellipsis = EllipsisLine(layoutParameters,
1766 layoutBidiParameters,
1770 glyphPositionsBuffer,
1773 isAutoScrollEnabled,
1778 //clear hyphen from ellipsis line
1779 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1780 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1782 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1784 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1785 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1789 // No more lines to layout.
1794 //In START location of ellipsis whether to shift lines or not.
1795 anyLineIsEliped |= ellipsis;
1797 // Whether the last line has been laid-out.
1798 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1800 if(numberOfLines == linesCapacity)
1802 // Reserve more space for the next lines.
1803 linesBuffer = ResizeLinesBuffer(lines,
1806 updateCurrentBuffer);
1809 // Updates the current text's layout with the line's layout.
1810 UpdateTextLayout(layoutParameters,
1818 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1820 if((nextIndex == totalNumberOfGlyphs) &&
1821 layoutParameters.isLastNewParagraph &&
1822 (mLayout == MULTI_LINE_BOX))
1824 // The last character of the text is a new paragraph character.
1825 // An extra line with no characters is added to increase the text's height
1826 // in order to place the cursor.
1828 if(numberOfLines == linesCapacity)
1830 // Reserve more space for the next lines.
1831 linesBuffer = ResizeLinesBuffer(lines,
1834 updateCurrentBuffer);
1837 UpdateTextLayout(layoutParameters,
1838 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1839 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1843 } // whether to add a last line.
1845 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1847 if((nullptr != bidirectionalLineInfo) &&
1848 !bidirectionalLineInfo->isIdentity &&
1849 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1851 SetGlyphPositions(layoutParameters,
1852 glyphPositionsBuffer,
1853 layoutBidiParameters,
1858 // Sets the positions of the glyphs.
1859 SetGlyphPositions(layoutParameters,
1860 glyphPositionsBuffer,
1864 // Updates the vertical pen's position.
1865 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender);
1867 // Increase the glyph index.
1870 } // end for() traversing glyphs.
1872 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1873 if(anyLineIsEliped && numberOfLines > 1u)
1875 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1877 Length lineIndex = 0;
1878 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1880 LineRun& delLine = linesBuffer[lineIndex];
1881 delLine.ellipsis = true;
1883 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1884 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1886 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1887 linesBuffer[lineIndex].ellipsis = false;
1891 linesBuffer[0u].ellipsis = true;
1893 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1895 Length middleLineIndex = (numberOfLines) / 2u;
1896 Length ellipsisLineIndex = 0u;
1897 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1899 LineRun& delLine = linesBuffer[middleLineIndex];
1900 delLine.ellipsis = true;
1902 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1903 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1905 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1906 linesBuffer[lineIndex].ellipsis = false;
1909 ellipsisLineIndex = middleLineIndex - 1u;
1910 middleLineIndex = (numberOfLines) / 2u;
1913 linesBuffer[ellipsisLineIndex].ellipsis = true;
1917 if(updateCurrentBuffer)
1919 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1920 newGlyphPositions.Begin(),
1921 newGlyphPositions.End());
1922 glyphPositions.Resize(totalNumberOfGlyphs);
1924 newLines.Resize(numberOfLines);
1926 // Current text's layout size adds only the newly laid-out lines.
1927 // Updates the layout size with the previously laid-out lines.
1928 UpdateLayoutSize(lines,
1931 if(0u != newLines.Count())
1933 const LineRun& lastLine = *(newLines.End() - 1u);
1935 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1936 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1938 // Update the indices of the runs before the new laid-out lines are inserted.
1939 UpdateLineIndexOffsets(layoutParameters,
1944 // Insert the lines.
1945 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1952 lines.Resize(numberOfLines);
1955 // Rounds upward to avoid a non integer size.
1956 layoutSize.height = std::ceil(layoutSize.height);
1958 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1963 void Align(const Size& size,
1964 CharacterIndex startIndex,
1965 Length numberOfCharacters,
1966 Text::HorizontalAlignment::Type horizontalAlignment,
1967 Vector<LineRun>& lines,
1968 float& alignmentOffset,
1969 Dali::LayoutDirection::Type layoutDirection,
1970 bool matchLayoutDirection)
1972 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1974 alignmentOffset = MAX_FLOAT;
1975 // Traverse all lines and align the glyphs.
1976 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1980 LineRun& line = *it;
1982 if(line.characterRun.characterIndex < startIndex)
1984 // Do not align lines which have already been aligned.
1988 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1990 // Do not align lines beyond the last laid-out character.
1994 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1996 // Do not align lines beyond the last laid-out character unless the line is last and empty.
2000 // Calculate the line's alignment offset accordingly with the align option,
2001 // the box width, line length, and the paragraph's direction.
2002 CalculateHorizontalAlignment(size.width,
2003 horizontalAlignment,
2006 matchLayoutDirection);
2008 // Updates the alignment offset.
2009 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2013 void CalculateHorizontalAlignment(float boxWidth,
2014 HorizontalAlignment::Type horizontalAlignment,
2016 Dali::LayoutDirection::Type layoutDirection,
2017 bool matchLayoutDirection)
2019 line.alignmentOffset = 0.f;
2020 const bool isLineRTL = RTL == line.direction;
2022 // Whether to swap the alignment.
2023 // 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.
2024 bool isLayoutRTL = isLineRTL;
2025 float lineLength = line.width;
2027 // match align for system language direction
2028 if(matchLayoutDirection)
2030 // Swap the alignment type if the line is right to left.
2031 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2033 // Calculate the horizontal line offset.
2034 switch(horizontalAlignment)
2036 case HorizontalAlignment::BEGIN:
2042 lineLength += line.extraLength;
2045 line.alignmentOffset = boxWidth - lineLength;
2049 line.alignmentOffset = 0.f;
2053 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2054 line.alignmentOffset -= line.extraLength;
2059 case HorizontalAlignment::CENTER:
2061 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2065 line.alignmentOffset -= line.extraLength;
2068 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2071 case HorizontalAlignment::END:
2075 line.alignmentOffset = 0.f;
2079 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2080 line.alignmentOffset -= line.extraLength;
2087 lineLength += line.extraLength;
2090 line.alignmentOffset = boxWidth - lineLength;
2097 void Initialize(LineRun& line)
2099 line.glyphRun.glyphIndex = 0u;
2100 line.glyphRun.numberOfGlyphs = 0u;
2101 line.characterRun.characterIndex = 0u;
2102 line.characterRun.numberOfCharacters = 0u;
2104 line.ascender = 0.f;
2105 line.descender = 0.f;
2106 line.extraLength = 0.f;
2107 line.alignmentOffset = 0.f;
2108 line.direction = LTR;
2109 line.ellipsis = false;
2110 line.lineSpacing = mDefaultLineSpacing;
2111 line.isSplitToTwoHalves = false;
2112 line.glyphRunSecondHalf.glyphIndex = 0u;
2113 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2114 line.characterRunForSecondHalfLine.characterIndex = 0u;
2115 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2120 float mDefaultLineSpacing;
2121 float mDefaultLineSize;
2123 IntrusivePtr<Metrics> mMetrics;
2124 float mRelativeLineSize;
2130 mImpl = new Engine::Impl();
2138 void Engine::SetMetrics(MetricsPtr& metrics)
2140 mImpl->mMetrics = metrics;
2143 void Engine::SetLayout(Type layout)
2145 mImpl->mLayout = layout;
2148 Engine::Type Engine::GetLayout() const
2150 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2151 return mImpl->mLayout;
2154 void Engine::SetCursorWidth(int width)
2156 mImpl->mCursorWidth = static_cast<float>(width);
2159 int Engine::GetCursorWidth() const
2161 return static_cast<int>(mImpl->mCursorWidth);
2164 bool Engine::LayoutText(Parameters& layoutParameters,
2166 bool elideTextEnabled,
2167 bool& isAutoScrollEnabled,
2168 DevelText::EllipsisPosition::Type ellipsisPosition)
2170 return mImpl->LayoutText(layoutParameters,
2173 isAutoScrollEnabled,
2177 void Engine::Align(const Size& size,
2178 CharacterIndex startIndex,
2179 Length numberOfCharacters,
2180 Text::HorizontalAlignment::Type horizontalAlignment,
2181 Vector<LineRun>& lines,
2182 float& alignmentOffset,
2183 Dali::LayoutDirection::Type layoutDirection,
2184 bool matchLayoutDirection)
2189 horizontalAlignment,
2193 matchLayoutDirection);
2196 void Engine::SetDefaultLineSpacing(float lineSpacing)
2198 mImpl->mDefaultLineSpacing = lineSpacing;
2201 float Engine::GetDefaultLineSpacing() const
2203 return mImpl->mDefaultLineSpacing;
2206 void Engine::SetDefaultLineSize(float lineSize)
2208 mImpl->mDefaultLineSize = lineSize;
2211 float Engine::GetDefaultLineSize() const
2213 return mImpl->mDefaultLineSize;
2216 void Engine::SetRelativeLineSize(float relativeLineSize)
2218 mImpl->mRelativeLineSize = relativeLineSize;
2221 float Engine::GetRelativeLineSize() const
2223 return mImpl->mRelativeLineSize;
2226 } // namespace Layout
2230 } // namespace Toolkit