2 * Copyright (c) 2024 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>
33 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
41 float GetLineHeight(const LineRun lineRun, bool isLastLine)
43 // The line height is the addition of the line ascender, the line descender and the line spacing.
44 // However, the line descender has a negative value, hence the subtraction.
45 // In case this is the only/last line then line spacing should be ignored.
46 float lineHeight = lineRun.ascender - lineRun.descender;
48 if(!isLastLine || lineRun.lineSpacing > 0)
50 lineHeight += lineRun.lineSpacing;
59 #if defined(DEBUG_ENABLED)
60 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
63 const float MAX_FLOAT = std::numeric_limits<float>::max();
64 const CharacterDirection LTR = false;
65 const CharacterDirection RTL = !LTR;
66 const float LINE_SPACING = 0.f;
67 const float MIN_LINE_SIZE = 0.f;
68 const Character HYPHEN_UNICODE = 0x002D;
69 const float RELATIVE_LINE_SIZE = 1.f;
71 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
73 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
79 * @brief Stores temporary layout info of the line.
87 numberOfCharacters{0u},
94 whiteSpaceLengthEndOfLine{0.f},
96 isSplitToTwoHalves(false),
97 glyphIndexInSecondHalfLine{0u},
98 characterIndexInSecondHalfLine{0u},
99 numberOfGlyphsInSecondHalfLine{0u},
100 numberOfCharactersInSecondHalfLine{0u},
101 relativeLineSize{1.0f}
115 numberOfCharacters = 0u;
116 ascender = -MAX_FLOAT;
117 descender = MAX_FLOAT;
119 isSplitToTwoHalves = false;
120 glyphIndexInSecondHalfLine = 0u;
121 characterIndexInSecondHalfLine = 0u;
122 numberOfGlyphsInSecondHalfLine = 0u;
123 numberOfCharactersInSecondHalfLine = 0u;
124 relativeLineSize = 1.0f;
127 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
128 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
129 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
130 Length numberOfCharacters; ///< The number of characters which fit in one line.
131 float ascender; ///< The maximum ascender of all fonts in the line.
132 float descender; ///< The minimum descender of all fonts in the line.
133 float lineSpacing; ///< The line spacing
134 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
135 float previousAdvance; ///< The advance of the previous glyph.
136 float length; ///< The current length of the line.
137 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
138 CharacterDirection direction;
140 bool isSplitToTwoHalves; ///< Whether the second half is defined.
141 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
142 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
143 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
144 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
146 float relativeLineSize; ///< The relative line size to be applied for this line.
149 struct LayoutBidiParameters
153 paragraphDirection = LTR;
154 bidiParagraphIndex = 0u;
156 isBidirectional = false;
159 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
160 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
161 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
162 bool isBidirectional = false; ///< Whether the text is bidirectional.
168 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
170 mDefaultLineSpacing{LINE_SPACING},
171 mDefaultLineSize{MIN_LINE_SIZE},
172 mRelativeLineSize{RELATIVE_LINE_SIZE}
177 * @brief get the line spacing.
179 * @param[in] textSize The text size.
180 * @param[in] relativeLineSize The relative line size to be applied.
181 * @return the line spacing value.
183 float GetLineSpacing(float textSize, float relativeLineSize)
188 // Sets the line size
189 lineSpacing = mDefaultLineSize - textSize;
190 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
192 // Add the line spacing
193 lineSpacing += mDefaultLineSpacing;
195 //subtract line spcaing if relativeLineSize < 1 & larger than min height
196 relTextSize = textSize * relativeLineSize;
197 if(relTextSize > mDefaultLineSize)
199 if(relativeLineSize < 1)
201 //subtract the difference (always will be positive)
202 lineSpacing -= (textSize - relTextSize);
206 //reverse the addition in the top.
207 if(mDefaultLineSize > textSize)
209 lineSpacing -= mDefaultLineSize - textSize;
212 //add difference instead
213 lineSpacing += relTextSize - textSize;
221 * @brief Updates the line ascender and descender with the metrics of a new font.
223 * @param[in] glyphMetrics The metrics of the new font.
224 * @param[in,out] lineLayout The line layout.
226 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
228 Text::FontMetrics fontMetrics;
229 if(0u != glyphMetrics.fontId)
231 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
235 fontMetrics.ascender = glyphMetrics.fontHeight;
236 fontMetrics.descender = 0.f;
237 fontMetrics.height = fontMetrics.ascender;
238 fontMetrics.underlinePosition = 0.f;
239 fontMetrics.underlineThickness = 1.f;
242 // Sets the maximum ascender.
243 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
245 // Sets the minimum descender.
246 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
248 lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender, lineLayout.relativeLineSize);
252 * @brief Merges a temporary line layout into the line layout.
254 * @param[in,out] lineLayout The line layout.
255 * @param[in] tmpLineLayout A temporary line layout.
256 * @param[in] isShifted Whether to shift first glyph and character indices.
258 void MergeLineLayout(LineLayout& lineLayout,
259 const LineLayout& tmpLineLayout,
262 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
263 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
265 lineLayout.penX = tmpLineLayout.penX;
266 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
268 lineLayout.length = tmpLineLayout.length;
269 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
271 // Sets the maximum ascender.
272 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
274 // Sets the minimum descender.
275 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
277 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
280 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
281 lineLayout.characterIndex = tmpLineLayout.characterIndex;
284 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
285 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
286 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
287 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
288 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
291 void LayoutRightToLeft(const Parameters& parameters,
292 const BidirectionalLineInfoRun& bidirectionalLineInfo,
294 float& whiteSpaceLengthEndOfLine)
296 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
297 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
298 // then the first half of line "characterRun",
299 // Otherwise travers whole characters in"characterRun".
301 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
302 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
303 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
304 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
306 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
307 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
308 const float modelCharacterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
310 // Get the character-spacing runs.
311 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
313 CharacterIndex characterLogicalIndex = 0u;
314 CharacterIndex characterVisualIndex = 0u;
316 float calculatedAdvance = 0.f;
318 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
319 // Otherwise maps the first visual index from visualToLogicalMap.
320 // This is to initialize the first visual index.
321 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
323 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
327 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
330 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
332 if(RTL == bidirectionalLineInfo.direction)
334 // If there are characters in the second half of Line.
335 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
337 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
338 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
340 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
342 const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
343 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
344 whiteSpaceLengthEndOfLine += calculatedAdvance;
346 ++characterLogicalIndex;
347 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
351 // If all characters in the second half of Line are WhiteSpaces.
352 // then continue adding the WhiteSpaces from the first hel of Line.
353 // Also this is valid when the line was not splitted.
354 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
356 extendedToSecondHalf = true; // Whether the logical index is extended to second half
357 characterLogicalIndex = 0u;
358 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
360 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
361 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
363 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
365 const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
366 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
367 whiteSpaceLengthEndOfLine += calculatedAdvance;
369 ++characterLogicalIndex;
370 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
375 // Here's the first index of character is not WhiteSpace
376 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
378 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
379 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
380 lastGlyphOfParagraphPlusOne,
381 charactersPerGlyphBuffer);
383 GlyphMetrics glyphMetrics;
384 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
385 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
386 GetGlyphsMetrics(glyphIndex,
387 numberOfGLyphsInGroup,
393 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
395 // Traverses the characters of the right to left paragraph.
396 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
397 if(!extendedToSecondHalf &&
398 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
400 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
402 // Convert the character in the logical order into the character in the visual order.
403 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
404 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
406 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
408 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
409 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
410 lastGlyphOfParagraphPlusOne,
411 charactersPerGlyphBuffer);
413 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
415 GlyphMetrics glyphMetrics;
416 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
417 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
418 GetGlyphsMetrics(glyphIndex,
419 numberOfGLyphsInGroup,
427 // If glyph is WhiteSpace then:
428 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
429 // the endOfLine in RTL was the headOfLine for layouting.
430 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
431 if(RTL == bidirectionalLineInfo.direction)
433 length += glyphMetrics.advance;
437 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
439 penX += glyphMetrics.advance;
443 // If glyph is not whiteSpace then:
444 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
445 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
446 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
447 // Otherwise the current length is maximum.
448 if(LTR == bidirectionalLineInfo.direction)
450 whiteSpaceLengthEndOfLine = 0.f;
453 if(parameters.textModel->mRemoveBackInset)
455 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
459 length = std::max(length, penX + glyphMetrics.advance);
462 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
467 // Continue traversing in the first half of line or in the whole line.
468 // If the second half of line was extended then continue from logical index in the first half of line
469 // Also this is valid when the line was not splitted and there were WhiteSpace.
470 // Otherwise start from first logical index in line.
471 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
472 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
474 // Convert the character in the logical order into the character in the visual order.
475 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
476 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
478 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
480 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
481 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
482 lastGlyphOfParagraphPlusOne,
483 charactersPerGlyphBuffer);
485 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
487 GlyphMetrics glyphMetrics;
488 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
489 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
490 GetGlyphsMetrics(glyphIndex,
491 numberOfGLyphsInGroup,
499 // If glyph is WhiteSpace then:
500 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
501 // the endOfLine in RTL was the headOfLine for layouting.
502 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
503 if(RTL == bidirectionalLineInfo.direction)
505 length += glyphMetrics.advance;
509 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
511 penX += glyphMetrics.advance;
515 // If glyph is not whiteSpace then:
516 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
517 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
518 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
519 // Otherwise the current length is maximum.
520 if(LTR == bidirectionalLineInfo.direction)
522 whiteSpaceLengthEndOfLine = 0.f;
525 if(parameters.textModel->mRemoveBackInset)
527 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
531 length = std::max(length, penX + glyphMetrics.advance);
533 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
538 void ReorderBiDiLayout(const Parameters& parameters,
539 LayoutBidiParameters& bidiParameters,
540 const LineLayout& currentLineLayout,
541 LineLayout& lineLayout,
542 bool breakInCharacters,
543 bool enforceEllipsisInSingleLine)
545 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
547 // The last glyph to be laid-out.
548 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
550 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
552 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
553 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
554 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
556 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
558 // Sets the visual to logical map tables needed to reorder the text.
559 ReorderLine(bidirectionalParagraphInfo,
560 bidirectionalLinesInfo,
561 bidiParameters.bidiLineIndex,
562 lineLayout.characterIndex,
563 lineLayout.numberOfCharacters,
564 lineLayout.characterIndexInSecondHalfLine,
565 lineLayout.numberOfCharactersInSecondHalfLine,
566 bidiParameters.paragraphDirection);
568 // Recalculate the length of the line and update the layout.
569 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
571 if(!bidirectionalLineInfo.isIdentity)
574 float whiteSpaceLengthEndOfLine = 0.f;
575 LayoutRightToLeft(parameters,
576 bidirectionalLineInfo,
578 whiteSpaceLengthEndOfLine);
580 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
581 if(!Equals(length, lineLayout.length))
583 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
585 if(isMultiline && (length > parameters.boundingBox.width))
587 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
589 // The word doesn't fit in one line. It has to be split by character.
591 // Remove the last laid out glyph(s) as they doesn't fit.
592 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
594 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
595 lastGlyphOfParagraphPlusOne,
596 charactersPerGlyphBuffer);
598 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
600 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
601 lineLayout.numberOfCharacters -= numberOfCharacters;
603 AdjustLayout(parameters,
605 bidirectionalParagraphInfo,
608 if(lineLayout.length < parameters.boundingBox.width)
613 if(glyphIndex < numberOfGLyphsInGroup)
615 // avoids go under zero for an unsigned int.
619 glyphIndex -= numberOfGLyphsInGroup;
624 lineLayout = currentLineLayout;
626 AdjustLayout(parameters,
628 bidirectionalParagraphInfo,
634 lineLayout.length = std::max(length, lineLayout.length);
641 void AdjustLayout(const Parameters& parameters,
642 LayoutBidiParameters& bidiParameters,
643 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
644 LineLayout& lineLayout)
646 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
648 // Remove current reordered line.
649 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
651 // Re-build the conversion table without the removed glyphs.
652 ReorderLine(bidirectionalParagraphInfo,
653 bidirectionalLinesInfo,
654 bidiParameters.bidiLineIndex,
655 lineLayout.characterIndex,
656 lineLayout.numberOfCharacters,
657 lineLayout.characterIndexInSecondHalfLine,
658 lineLayout.numberOfCharactersInSecondHalfLine,
659 bidiParameters.paragraphDirection);
661 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
664 float whiteSpaceLengthEndOfLine = 0.f;
665 LayoutRightToLeft(parameters,
666 bidirectionalLineInfo,
668 whiteSpaceLengthEndOfLine);
670 lineLayout.length = length;
671 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
675 * Retrieves the line layout for a given box width.
677 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
678 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
679 * to reorder the line and recalculate its length.
682 * @param[in] parameters The layout parameters.
683 * @param[] bidiParameters Bidirectional info for the current line.
684 * @param[out] lineLayout The line layout.
685 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
686 * @param[in] ellipsisPosition Where is the location the text elide
687 * @param[in] hiddenInputEnabled Whether the hidden input is enabled.
689 void GetLineLayoutForBox(const Parameters& parameters,
690 LayoutBidiParameters& bidiParameters,
691 LineLayout& lineLayout,
693 DevelText::EllipsisPosition::Type ellipsisPosition,
694 bool enforceEllipsisInSingleLine,
695 bool elideTextEnabled,
696 bool hiddenInputEnabled)
698 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
699 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
701 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
702 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
703 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
704 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
705 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
707 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
708 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
710 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
711 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
712 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
713 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
714 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
715 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
717 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
719 // The last glyph to be laid-out.
720 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
722 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
723 // In the case the line starts with a right to left character, if the width is longer than the advance,
724 // the difference needs to be added to the line length.
726 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
727 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
728 lastGlyphOfParagraphPlusOne,
729 charactersPerGlyphBuffer);
731 float targetWidth = parameters.boundingBox.width;
732 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
734 bool isSecondHalf = false;
736 const float modelCharacterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
737 float calculatedAdvance = 0.f;
738 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
739 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
741 // Get the character-spacing runs.
742 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
744 GlyphMetrics glyphMetrics;
745 const float characterSpacing = GetGlyphCharacterSpacing(lineLayout.glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
746 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
747 GetGlyphsMetrics(lineLayout.glyphIndex,
748 numberOfGLyphsInGroup,
754 // Set the direction of the first character of the line.
755 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
757 // Stores temporary line layout which has not been added to the final line layout.
758 LineLayout tmpLineLayout;
760 // Initialize the start point.
762 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
763 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
764 // 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.
766 tmpLineLayout.penX = mCursorWidth + outlineWidth;
767 if(parameters.textModel->mRemoveFrontInset)
769 tmpLineLayout.penX -= glyphMetrics.xBearing;
772 tmpLineLayout.relativeLineSize = lineLayout.relativeLineSize;
774 // Calculate the line height if there is no characters.
775 FontId lastFontId = glyphMetrics.fontId;
776 UpdateLineHeight(glyphMetrics, tmpLineLayout);
778 bool oneWordLaidOut = false;
779 bool oneHyphenLaidOut = false;
780 GlyphIndex hyphenIndex = 0;
781 GlyphInfo hyphenGlyph;
783 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
784 glyphIndex < lastGlyphOfParagraphPlusOne;)
786 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
788 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
789 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
790 lastGlyphOfParagraphPlusOne,
791 charactersPerGlyphBuffer);
793 GlyphMetrics glyphMetrics;
794 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
795 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
796 GetGlyphsMetrics(glyphIndex,
797 numberOfGLyphsInGroup,
803 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
805 // Check if the font of the current glyph is the same of the previous one.
806 // If it's different the ascender and descender need to be updated.
807 if(lastFontId != glyphMetrics.fontId)
809 UpdateLineHeight(glyphMetrics, tmpLineLayout);
810 lastFontId = glyphMetrics.fontId;
813 // Get the character indices for the current glyph. The last character index is needed
814 // because there are glyphs formed by more than one character but their break info is
815 // given only for the last character.
816 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
817 const bool hasCharacters = charactersPerGlyph > 0u;
818 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
819 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
821 // Get the line break info for the current character.
822 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
826 // Increase the number of characters.
827 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
829 // Increase the number of glyphs.
830 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
834 // Increase the number of characters.
835 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
837 // Increase the number of glyphs.
838 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
841 // Check whether is a white space.
842 const Character character = *(textBuffer + characterFirstIndex);
843 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
845 // Calculate the length of the line.
847 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
848 const float previousTmpPenX = tmpLineLayout.penX;
849 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
850 const float previousTmpLength = tmpLineLayout.length;
851 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
853 // The calculated text size is used in atlas renderer.
854 // When the text is all white space, partial render issue occurs because the width is 0.
855 // To avoid issue, do not remove the white space size in hidden input mode.
856 if(isWhiteSpace && !hiddenInputEnabled)
858 // Add the length to the length of white spaces at the end of the line.
859 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
860 // The advance is used as the width is always zero for the white spaces.
864 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
865 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
867 if(parameters.textModel->mRemoveBackInset)
869 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
873 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.advance);
876 // Clear the white space length at the end of the line.
877 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
880 if(isSplitToTwoHalves && (!isSecondHalf) &&
881 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
883 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
884 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
886 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
887 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
889 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
890 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
892 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
894 // Check if the accumulated length fits in the width of the box.
895 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
896 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
897 completelyFill && !isMultiline &&
898 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
900 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
902 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
904 GlyphMetrics glyphMetrics;
905 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndexToRemove, characterSpacingGlyphRuns, modelCharacterSpacing);
906 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
907 GetGlyphsMetrics(glyphIndexToRemove,
908 numberOfGLyphsInGroup,
914 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
915 lastGlyphOfParagraphPlusOne,
916 charactersPerGlyphBuffer);
918 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
919 const bool hasCharacters = charactersPerGlyph > 0u;
920 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
921 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
923 // Check whether is a white space.
924 const Character character = *(textBuffer + characterFirstIndex);
925 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
929 // Decrease the number of characters for SecondHalf.
930 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
932 // Decrease the number of glyphs for SecondHalf.
933 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
937 // Decrease the number of characters.
938 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
940 // Decrease the number of glyphs.
941 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
944 if(isRemovedGlyphWhiteSpace && !hiddenInputEnabled)
946 tmpLineLayout.penX -= glyphMetrics.advance;
947 tmpLineLayout.length -= glyphMetrics.advance;
951 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
952 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
957 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
958 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
959 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
963 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
964 tmpLineLayout.characterIndex = characterLastIndex + 1u;
965 glyphIndexToRemove = tmpLineLayout.glyphIndex;
969 else if((completelyFill || isMultiline) &&
970 (tmpLineLayout.length > targetWidth))
972 // Current word does not fit in the box's width.
973 if(((oneHyphenLaidOut && isHyphenMode) ||
974 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
977 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
978 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
981 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
983 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
985 // The word doesn't fit in the control's width. It needs to be split by character.
986 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
990 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
991 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
995 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
996 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
999 tmpLineLayout.penX = previousTmpPenX;
1000 tmpLineLayout.previousAdvance = previousTmpAdvance;
1001 tmpLineLayout.length = previousTmpLength;
1002 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
1005 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1007 // Add part of the word to the line layout and shift the first glyph.
1008 MergeLineLayout(lineLayout, tmpLineLayout, true);
1010 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
1011 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
1013 // Add part of the word to the line layout.
1014 MergeLineLayout(lineLayout, tmpLineLayout, false);
1019 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
1022 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
1024 // Reorder the RTL line.
1025 if(bidiParameters.isBidirectional)
1027 ReorderBiDiLayout(parameters,
1032 enforceEllipsisInSingleLine);
1038 if((isMultiline || isLastGlyph) &&
1039 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
1041 LineLayout currentLineLayout = lineLayout;
1042 oneHyphenLaidOut = false;
1044 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1046 // Must break the line. Update the line layout, shift the first glyph and return.
1047 MergeLineLayout(lineLayout, tmpLineLayout, true);
1051 // Must break the line. Update the line layout and return.
1052 MergeLineLayout(lineLayout, tmpLineLayout, false);
1055 // Reorder the RTL line.
1056 if(bidiParameters.isBidirectional)
1058 ReorderBiDiLayout(parameters,
1063 enforceEllipsisInSingleLine);
1066 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
1067 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1073 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1075 oneHyphenLaidOut = false;
1076 oneWordLaidOut = isWordLaidOut;
1077 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
1079 // Current glyph is the last one of the current word.
1080 // Add the temporal layout to the current one.
1081 MergeLineLayout(lineLayout, tmpLineLayout, false);
1083 tmpLineLayout.Clear();
1087 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1088 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1090 hyphenGlyph = GlyphInfo();
1091 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1093 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1094 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1096 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1098 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1100 hyphenIndex = glyphIndex;
1101 oneHyphenLaidOut = true;
1103 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1105 // Current glyph is the last one of the current word hyphen.
1106 // Add the temporal layout to the current one.
1107 MergeLineLayout(lineLayout, tmpLineLayout, false);
1109 tmpLineLayout.Clear();
1113 glyphIndex += numberOfGLyphsInGroup;
1116 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1119 void SetGlyphPositions(const Parameters& layoutParameters,
1120 Vector2* glyphPositionsBuffer,
1121 const LineLayout& layout)
1123 // Traverse the glyphs and set the positions.
1125 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1126 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1127 const Length numberOfGlyphs = layout.numberOfGlyphs;
1128 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1130 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1131 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1133 // Check if the x bearing of the first character is negative.
1134 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1135 // so the penX position needs to be moved to the right.
1136 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1137 float penX = mCursorWidth + outlineWidth; //
1139 if(layoutParameters.textModel->mRemoveFrontInset)
1141 penX -= glyph.xBearing;
1144 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1145 layoutParameters.textModel->mLogicalModel,
1146 interGlyphExtraAdvance,
1148 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1149 startIndexForGlyphPositions,
1150 glyphPositionsBuffer,
1153 if(layout.isSplitToTwoHalves)
1155 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1156 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1157 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1159 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1160 layoutParameters.textModel->mLogicalModel,
1161 interGlyphExtraAdvance,
1162 numberOfGlyphsInSecondHalfLine,
1163 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1164 startIndexForGlyphPositionsnSecondHalf,
1165 glyphPositionsBuffer,
1170 void SetGlyphPositions(const Parameters& layoutParameters,
1171 Vector2* glyphPositionsBuffer,
1172 LayoutBidiParameters& layoutBidiParameters,
1173 const LineLayout& layout)
1175 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1176 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1177 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1179 CharacterIndex characterLogicalIndex = 0u;
1180 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1181 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1185 if(layout.isSplitToTwoHalves)
1187 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1188 layoutParameters.textModel->mLogicalModel,
1189 layoutBidiParameters.bidiLineIndex,
1190 layoutParameters.startGlyphIndex,
1191 glyphPositionsBuffer,
1192 characterVisualIndex,
1193 characterLogicalIndex,
1197 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1199 extendedToSecondHalf = true;
1200 characterLogicalIndex = 0u;
1201 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1203 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1204 layoutParameters.textModel->mLogicalModel,
1205 layoutBidiParameters.bidiLineIndex,
1206 layoutParameters.startGlyphIndex,
1207 glyphPositionsBuffer,
1208 characterVisualIndex,
1209 characterLogicalIndex,
1213 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1214 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1216 penX += -glyph.xBearing;
1218 // Traverses the characters of the right to left paragraph.
1219 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1221 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1222 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1223 layoutParameters.startGlyphIndex,
1224 layoutParameters.interGlyphExtraAdvance,
1225 bidiLine.characterRunForSecondHalfLine,
1226 bidiLine.visualToLogicalMapSecondHalf,
1227 glyphPositionsBuffer,
1228 characterLogicalIndex,
1232 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1234 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1235 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1236 layoutParameters.startGlyphIndex,
1237 layoutParameters.interGlyphExtraAdvance,
1238 bidiLine.characterRun,
1239 bidiLine.visualToLogicalMap,
1240 glyphPositionsBuffer,
1241 characterLogicalIndex,
1246 * @brief Resizes the line buffer.
1248 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1249 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1250 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1251 * @param[in] updateCurrentBuffer Whether the layout is updated.
1253 * @return Pointer to either lines or newLines.
1255 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1256 Vector<LineRun>& newLines,
1257 Length& linesCapacity,
1258 bool updateCurrentBuffer)
1260 LineRun* linesBuffer = nullptr;
1261 // Reserve more space for the next lines.
1262 linesCapacity *= 2u;
1263 if(updateCurrentBuffer)
1265 newLines.Resize(linesCapacity);
1266 linesBuffer = newLines.Begin();
1270 lines.Resize(linesCapacity);
1271 linesBuffer = lines.Begin();
1278 * Ellipsis a line if it exceeds the width's of the bounding box.
1280 * @param[in] layoutParameters The parameters needed to layout the text.
1281 * @param[in] layout The line layout.
1282 * @param[in,out] layoutSize The text's layout size.
1283 * @param[in,out] linesBuffer Pointer to the line's buffer.
1284 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1285 * @param[in,out] numberOfLines The number of laid-out lines.
1286 * @param[in] penY The vertical layout position.
1287 * @param[in] currentParagraphDirection The current paragraph's direction.
1288 * @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
1289 * @param[in] isHiddenInputEnabled Whether the hidden input is enabled.
1290 * @param[in] ellipsisPosition Where is the location the text elide
1292 * return Whether the line is ellipsized.
1294 bool EllipsisLine(const Parameters& layoutParameters,
1295 LayoutBidiParameters& layoutBidiParameters,
1296 const LineLayout& layout,
1298 LineRun* linesBuffer,
1299 Vector2* glyphPositionsBuffer,
1300 Length& numberOfLines,
1302 bool& isAutoScrollEnabled,
1303 bool isAutoScrollMaxTextureExceeded,
1304 bool isHiddenInputEnabled,
1305 DevelText::EllipsisPosition::Type ellipsisPosition,
1306 bool enforceEllipsisInSingleLine)
1308 const bool ellipsis = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? isAutoScrollMaxTextureExceeded : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
1309 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1310 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1312 if(penY - layout.descender > layoutParameters.boundingBox.height)
1314 // Even if auto scroll is enabled and text is bigger than max texture size,
1315 // if the the height is small, auto scroll should not work.
1316 isAutoScrollEnabled = false;
1319 // Do not layout more lines if ellipsis is enabled.
1320 // The last line needs to be completely filled with characters.
1321 // Part of a word may be used.
1323 LineRun* lineRun = nullptr;
1324 LineLayout ellipsisLayout;
1326 ellipsisLayout.relativeLineSize = layout.relativeLineSize;
1328 if(0u != numberOfLines)
1330 // Get the last line and layout it again with the 'completelyFill' flag to true.
1331 lineRun = linesBuffer + (numberOfLines - 1u);
1332 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1334 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1338 // At least there is space reserved for one line.
1339 lineRun = linesBuffer;
1341 lineRun->glyphRun.glyphIndex = 0u;
1342 ellipsisLayout.glyphIndex = 0u;
1343 lineRun->isSplitToTwoHalves = false;
1348 GetLineLayoutForBox(layoutParameters,
1349 layoutBidiParameters,
1353 enforceEllipsisInSingleLine,
1355 isHiddenInputEnabled);
1357 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1359 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1362 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1363 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1364 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1365 lineRun->width = ellipsisLayout.length;
1366 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1367 lineRun->ascender = ellipsisLayout.ascender;
1368 lineRun->descender = ellipsisLayout.descender;
1369 lineRun->ellipsis = true;
1371 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1372 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1373 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1374 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1375 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1377 layoutSize.width = layoutParameters.boundingBox.width;
1378 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1380 layoutSize.height += GetLineHeight(*lineRun, true);
1384 //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1385 //usually we don't includ it in normal cases using GetLineHeight()
1386 if(lineRun->lineSpacing < 0)
1388 layoutSize.height -= lineRun->lineSpacing;
1392 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1394 if(layoutBidiParameters.isBidirectional)
1396 layoutBidiParameters.bidiLineIndex = 0u;
1397 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1398 endIt = bidirectionalLinesInfo.End();
1400 ++it, ++layoutBidiParameters.bidiLineIndex)
1402 const BidirectionalLineInfoRun& run = *it;
1403 //To handle case when the laid characters exist in next line.
1404 //More than one BidirectionalLineInfoRun could start with same character.
1405 //When need to check also numberOfCharacters in line.
1406 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1407 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1408 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1409 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1410 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1412 // Found where to insert the bidi line info.
1418 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1420 if((nullptr != bidirectionalLineInfo) &&
1421 !bidirectionalLineInfo->isIdentity &&
1422 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1424 lineRun->direction = RTL;
1425 SetGlyphPositions(layoutParameters,
1426 glyphPositionsBuffer,
1427 layoutBidiParameters,
1432 lineRun->direction = LTR;
1433 SetGlyphPositions(layoutParameters,
1434 glyphPositionsBuffer,
1443 * @brief Updates the text layout with a new laid-out line.
1445 * @param[in] layoutParameters The parameters needed to layout the text.
1446 * @param[in] layout The line layout.
1447 * @param[in,out] layoutSize The text's layout size.
1448 * @param[in,out] linesBuffer Pointer to the line's buffer.
1449 * @param[in] index Index to the vector of glyphs.
1450 * @param[in,out] numberOfLines The number of laid-out lines.
1451 * @param[in] isLastLine Whether the laid-out line is the last one.
1453 void UpdateTextLayout(const Parameters& layoutParameters,
1454 const LineLayout& layout,
1456 LineRun* linesBuffer,
1458 Length& numberOfLines,
1461 LineRun& lineRun = *(linesBuffer + numberOfLines);
1464 lineRun.glyphRun.glyphIndex = index;
1465 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1466 lineRun.characterRun.characterIndex = layout.characterIndex;
1467 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1468 lineRun.width = layout.length;
1469 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1471 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1472 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1473 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1474 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1475 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1477 // Rounds upward to avoid a non integer size.
1478 lineRun.width = std::ceil(lineRun.width);
1480 lineRun.ascender = layout.ascender;
1481 lineRun.descender = layout.descender;
1482 lineRun.direction = layout.direction;
1483 lineRun.ellipsis = false;
1485 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
1487 // Update the actual size.
1488 if(lineRun.width > layoutSize.width)
1490 layoutSize.width = lineRun.width;
1493 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1497 * @brief Updates the text layout with the last laid-out line.
1499 * @param[in] layoutParameters The parameters needed to layout the text.
1500 * @param[in] characterIndex The character index of the line.
1501 * @param[in] glyphIndex The glyph index of the line.
1502 * @param[in,out] layoutSize The text's layout size.
1503 * @param[in,out] linesBuffer Pointer to the line's buffer.
1504 * @param[in,out] numberOfLines The number of laid-out lines.
1506 void UpdateTextLayout(const Parameters& layoutParameters,
1507 CharacterIndex characterIndex,
1508 GlyphIndex glyphIndex,
1510 LineRun* linesBuffer,
1511 Length& numberOfLines)
1513 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1515 if(glyphs.Size() == 0u)
1521 // Need to add a new line with no characters but with height to increase the layoutSize.height
1522 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1524 Text::FontMetrics fontMetrics;
1525 if(0u != glyphInfo.fontId)
1527 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1530 LineRun& lineRun = *(linesBuffer + numberOfLines);
1533 lineRun.glyphRun.glyphIndex = glyphIndex;
1534 lineRun.glyphRun.numberOfGlyphs = 0u;
1535 lineRun.characterRun.characterIndex = characterIndex;
1536 lineRun.characterRun.numberOfCharacters = 0u;
1537 lineRun.width = 0.f;
1538 lineRun.ascender = fontMetrics.ascender;
1539 lineRun.descender = fontMetrics.descender;
1540 lineRun.extraLength = 0.f;
1541 lineRun.alignmentOffset = 0.f;
1542 lineRun.direction = LTR;
1543 lineRun.ellipsis = false;
1545 BoundedParagraphRun currentParagraphRun;
1546 LineLayout tempLineLayout;
1547 (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
1549 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
1551 layoutSize.height += GetLineHeight(lineRun, true);
1555 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1557 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1558 * @param[in,out] layoutSize The text's layout size.
1560 void UpdateLayoutSize(const Vector<LineRun>& lines,
1563 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1564 endIt = lines.End();
1568 const LineRun& line = *it;
1569 bool isLastLine = (it + 1 == endIt);
1571 if(line.width > layoutSize.width)
1573 layoutSize.width = line.width;
1576 layoutSize.height += GetLineHeight(line, isLastLine);
1581 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1583 * @param[in] layoutParameters The parameters needed to layout the text.
1584 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1585 * @param[in] characterOffset The offset to be added to the runs of characters.
1586 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1588 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1589 Vector<LineRun>& lines,
1590 Length characterOffset,
1593 // Update the glyph and character runs.
1594 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1595 endIt = lines.End();
1599 LineRun& line = *it;
1601 line.glyphRun.glyphIndex = glyphOffset;
1602 line.characterRun.characterIndex = characterOffset;
1604 glyphOffset += line.glyphRun.numberOfGlyphs;
1605 characterOffset += line.characterRun.numberOfCharacters;
1610 * @brief Sets the relative line size for the LineLayout
1612 * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
1613 * @param[in,out] lineLayout The line layout to be updated.
1615 void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
1617 lineLayout.relativeLineSize = mRelativeLineSize;
1619 if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
1621 lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
1626 * @brief Get the bounded paragraph for the characterIndex if exists.
1628 * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
1629 * @param[in] characterIndex The character index to get bounded paragraph for.
1630 * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
1632 * @return returns true if a bounded paragraph was found.
1634 bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
1636 for(Vector<BoundedParagraphRun>::Iterator it = boundedParagraphRuns.Begin(),
1637 endIt = boundedParagraphRuns.End();
1641 BoundedParagraphRun& tempParagraphRun = *it;
1643 if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
1644 characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
1646 currentParagraphRun = tempParagraphRun;
1654 bool LayoutText(Parameters& layoutParameters,
1656 bool elideTextEnabled,
1657 bool& isAutoScrollEnabled,
1658 bool isAutoScrollMaxTextureExceeded,
1659 bool isHiddenInputEnabled,
1660 DevelText::EllipsisPosition::Type ellipsisPosition)
1662 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1663 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1665 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1666 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1668 //Reset indices of ElidedGlyphs
1669 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1670 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1671 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1672 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1674 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1675 const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
1677 if(0u == layoutParameters.numberOfGlyphs)
1679 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1680 if(layoutParameters.isLastNewParagraph)
1682 Length numberOfLines = lines.Count();
1683 if(0u != numberOfLines)
1685 const LineRun& lastLine = *(lines.End() - 1u);
1687 if(0u != lastLine.characterRun.numberOfCharacters)
1689 // Need to add a new line with no characters but with height to increase the layoutSize.height
1691 Initialize(newLine);
1692 lines.PushBack(newLine);
1694 UpdateTextLayout(layoutParameters,
1695 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1696 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1704 // Calculates the layout size.
1705 UpdateLayoutSize(lines,
1708 // Rounds upward to avoid a non integer size.
1709 layoutSize.height = std::ceil(layoutSize.height);
1711 // Nothing else do if there are no glyphs to layout.
1715 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1716 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1717 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1719 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1720 // This extra line needs to be removed.
1721 if(0u != lines.Count())
1723 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1725 if((0u == lastLine->characterRun.numberOfCharacters) &&
1726 (lastGlyphPlusOne == totalNumberOfGlyphs))
1728 lines.Remove(lastLine);
1732 // Retrieve BiDi info.
1733 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1735 const CharacterIndex* const glyphsToCharactersBuffer = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
1736 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1737 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1739 // Set the layout bidirectional paramters.
1740 LayoutBidiParameters layoutBidiParameters;
1742 // Whether the layout is being updated or set from scratch.
1743 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1745 Vector2* glyphPositionsBuffer = nullptr;
1746 Vector<Vector2> newGlyphPositions;
1748 LineRun* linesBuffer = nullptr;
1749 Vector<LineRun> newLines;
1751 // Estimate the number of lines.
1752 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1753 Length numberOfLines = 0u;
1755 if(updateCurrentBuffer)
1757 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1758 glyphPositionsBuffer = newGlyphPositions.Begin();
1760 newLines.Resize(linesCapacity);
1761 linesBuffer = newLines.Begin();
1765 glyphPositionsBuffer = glyphPositions.Begin();
1767 lines.Resize(linesCapacity);
1768 linesBuffer = lines.Begin();
1771 float penY = CalculateLineOffset(lines,
1772 layoutParameters.startLineIndex);
1773 bool anyLineIsEliped = false;
1774 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1776 layoutBidiParameters.Clear();
1778 if(hasBidiParagraphs)
1780 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1782 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1783 endIt = bidirectionalParagraphsInfo.End();
1785 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1787 const BidirectionalParagraphInfoRun& run = *it;
1789 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1791 if(lastCharacterIndex <= startCharacterIndex)
1793 // Do not process, the paragraph has already been processed.
1797 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1799 layoutBidiParameters.paragraphDirection = run.direction;
1800 layoutBidiParameters.isBidirectional = true;
1803 // Has already been found.
1807 if(layoutBidiParameters.isBidirectional)
1809 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1810 endIt = bidirectionalLinesInfo.End();
1812 ++it, ++layoutBidiParameters.bidiLineIndex)
1814 const BidirectionalLineInfoRun& run = *it;
1816 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1818 if(lastCharacterIndex <= startCharacterIndex)
1824 if(startCharacterIndex < lastCharacterIndex)
1826 // Found where to insert the bidi line info.
1833 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1835 // Get the layout for the line.
1837 layout.direction = layoutBidiParameters.paragraphDirection;
1838 layout.glyphIndex = index;
1840 BoundedParagraphRun currentParagraphRun;
1841 (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
1843 GetLineLayoutForBox(layoutParameters,
1844 layoutBidiParameters,
1850 isHiddenInputEnabled);
1852 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1853 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1854 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1855 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1856 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1858 CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
1860 //check if this is the last line in paragraph, if false we should use the default relative line size (the one set using the property)
1861 if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex + layout.numberOfCharacters)
1863 layout.relativeLineSize = mRelativeLineSize;
1866 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1868 // The width is too small and no characters are laid-out.
1869 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1871 lines.Resize(numberOfLines);
1873 // Rounds upward to avoid a non integer size.
1874 layoutSize.height = std::ceil(layoutSize.height);
1879 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1881 penY += layout.ascender;
1883 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1885 bool ellipsis = false;
1886 if(elideTextEnabled)
1888 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1890 // Does the ellipsis of the last line.
1891 ellipsis = EllipsisLine(layoutParameters,
1892 layoutBidiParameters,
1896 glyphPositionsBuffer,
1899 isAutoScrollEnabled,
1900 isAutoScrollMaxTextureExceeded,
1901 isHiddenInputEnabled,
1906 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1908 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1909 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1911 ellipsis = EllipsisLine(layoutParameters,
1912 layoutBidiParameters,
1916 glyphPositionsBuffer,
1919 isAutoScrollEnabled,
1920 isAutoScrollMaxTextureExceeded,
1921 isHiddenInputEnabled,
1926 //clear hyphen from ellipsis line
1927 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1928 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1930 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1932 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1933 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1937 // No more lines to layout.
1942 //In START location of ellipsis whether to shift lines or not.
1943 anyLineIsEliped |= ellipsis;
1945 // Whether the last line has been laid-out.
1946 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1948 if(numberOfLines == linesCapacity)
1950 // Reserve more space for the next lines.
1951 linesBuffer = ResizeLinesBuffer(lines,
1954 updateCurrentBuffer);
1957 // Updates the current text's layout with the line's layout.
1958 UpdateTextLayout(layoutParameters,
1966 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1968 if((nextIndex == totalNumberOfGlyphs) &&
1969 layoutParameters.isLastNewParagraph &&
1970 (mLayout == MULTI_LINE_BOX))
1972 // The last character of the text is a new paragraph character.
1973 // An extra line with no characters is added to increase the text's height
1974 // in order to place the cursor.
1976 if(numberOfLines == linesCapacity)
1978 // Reserve more space for the next lines.
1979 linesBuffer = ResizeLinesBuffer(lines,
1982 updateCurrentBuffer);
1985 UpdateTextLayout(layoutParameters,
1986 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1987 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1991 } // whether to add a last line.
1993 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1995 if((nullptr != bidirectionalLineInfo) &&
1996 !bidirectionalLineInfo->isIdentity &&
1997 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1999 SetGlyphPositions(layoutParameters,
2000 glyphPositionsBuffer,
2001 layoutBidiParameters,
2006 // Sets the positions of the glyphs.
2007 SetGlyphPositions(layoutParameters,
2008 glyphPositionsBuffer,
2012 // Updates the vertical pen's position.
2013 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
2015 // Increase the glyph index.
2018 } // end for() traversing glyphs.
2020 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
2021 if(anyLineIsEliped && numberOfLines > 1u)
2023 if(ellipsisPosition == DevelText::EllipsisPosition::START)
2025 Length lineIndex = 0;
2026 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
2028 LineRun& delLine = linesBuffer[lineIndex];
2029 delLine.ellipsis = true;
2031 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
2032 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
2034 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
2035 linesBuffer[lineIndex].ellipsis = false;
2039 linesBuffer[0u].ellipsis = true;
2041 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
2043 Length middleLineIndex = (numberOfLines) / 2u;
2044 Length ellipsisLineIndex = 0u;
2045 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
2047 LineRun& delLine = linesBuffer[middleLineIndex];
2048 delLine.ellipsis = true;
2050 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
2051 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
2053 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
2054 linesBuffer[lineIndex].ellipsis = false;
2057 ellipsisLineIndex = middleLineIndex - 1u;
2058 middleLineIndex = (numberOfLines) / 2u;
2061 linesBuffer[ellipsisLineIndex].ellipsis = true;
2065 if(updateCurrentBuffer)
2067 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
2068 newGlyphPositions.Begin(),
2069 newGlyphPositions.End());
2070 glyphPositions.Resize(totalNumberOfGlyphs);
2072 newLines.Resize(numberOfLines);
2074 // Current text's layout size adds only the newly laid-out lines.
2075 // Updates the layout size with the previously laid-out lines.
2076 UpdateLayoutSize(lines,
2079 if(0u != newLines.Count())
2081 const LineRun& lastLine = *(newLines.End() - 1u);
2083 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
2084 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
2086 // Update the indices of the runs before the new laid-out lines are inserted.
2087 UpdateLineIndexOffsets(layoutParameters,
2092 // Insert the lines.
2093 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
2100 lines.Resize(numberOfLines);
2103 // Rounds upward to avoid a non integer size.
2104 layoutSize.height = std::ceil(layoutSize.height);
2106 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
2111 void Align(const Size& size,
2112 CharacterIndex startIndex,
2113 Length numberOfCharacters,
2114 Text::HorizontalAlignment::Type horizontalAlignment,
2115 Vector<LineRun>& lines,
2116 float& alignmentOffset,
2117 Dali::LayoutDirection::Type layoutDirection,
2118 bool matchLayoutDirection)
2120 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
2122 alignmentOffset = MAX_FLOAT;
2123 // Traverse all lines and align the glyphs.
2124 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
2128 LineRun& line = *it;
2130 if(line.characterRun.characterIndex < startIndex)
2132 // Do not align lines which have already been aligned.
2136 if(line.characterRun.characterIndex > lastCharacterPlusOne)
2138 // Do not align lines beyond the last laid-out character.
2142 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
2144 // Do not align lines beyond the last laid-out character unless the line is last and empty.
2148 // Calculate the line's alignment offset accordingly with the align option,
2149 // the box width, line length, and the paragraph's direction.
2150 CalculateHorizontalAlignment(size.width,
2151 horizontalAlignment,
2154 matchLayoutDirection);
2156 // Updates the alignment offset.
2157 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2161 void CalculateHorizontalAlignment(float boxWidth,
2162 HorizontalAlignment::Type horizontalAlignment,
2164 Dali::LayoutDirection::Type layoutDirection,
2165 bool matchLayoutDirection)
2167 line.alignmentOffset = 0.f;
2168 const bool isLineRTL = RTL == line.direction;
2170 // Whether to swap the alignment.
2171 // 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.
2172 bool isLayoutRTL = isLineRTL;
2173 float lineLength = line.width;
2175 // match align for system language direction
2176 if(matchLayoutDirection)
2178 // Swap the alignment type if the line is right to left.
2179 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2181 // Calculate the horizontal line offset.
2182 switch(horizontalAlignment)
2184 case HorizontalAlignment::BEGIN:
2190 lineLength += line.extraLength;
2193 line.alignmentOffset = boxWidth - lineLength;
2197 line.alignmentOffset = 0.f;
2201 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2202 line.alignmentOffset -= line.extraLength;
2207 case HorizontalAlignment::CENTER:
2209 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2213 line.alignmentOffset -= line.extraLength;
2216 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2219 case HorizontalAlignment::END:
2223 line.alignmentOffset = 0.f;
2227 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2228 line.alignmentOffset -= line.extraLength;
2235 lineLength += line.extraLength;
2238 line.alignmentOffset = boxWidth - lineLength;
2245 void Initialize(LineRun& line)
2247 line.glyphRun.glyphIndex = 0u;
2248 line.glyphRun.numberOfGlyphs = 0u;
2249 line.characterRun.characterIndex = 0u;
2250 line.characterRun.numberOfCharacters = 0u;
2252 line.ascender = 0.f;
2253 line.descender = 0.f;
2254 line.extraLength = 0.f;
2255 line.alignmentOffset = 0.f;
2256 line.direction = LTR;
2257 line.ellipsis = false;
2258 line.lineSpacing = mDefaultLineSpacing;
2259 line.isSplitToTwoHalves = false;
2260 line.glyphRunSecondHalf.glyphIndex = 0u;
2261 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2262 line.characterRunForSecondHalfLine.characterIndex = 0u;
2263 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2268 float mDefaultLineSpacing;
2269 float mDefaultLineSize;
2271 IntrusivePtr<Metrics> mMetrics;
2272 float mRelativeLineSize;
2278 mImpl = new Engine::Impl();
2286 void Engine::SetMetrics(MetricsPtr& metrics)
2288 mImpl->mMetrics = metrics;
2291 void Engine::SetLayout(Type layout)
2293 mImpl->mLayout = layout;
2296 Engine::Type Engine::GetLayout() const
2298 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2299 return mImpl->mLayout;
2302 void Engine::SetCursorWidth(int width)
2304 mImpl->mCursorWidth = static_cast<float>(width);
2307 int Engine::GetCursorWidth() const
2309 return static_cast<int>(mImpl->mCursorWidth);
2312 bool Engine::LayoutText(Parameters& layoutParameters,
2314 bool elideTextEnabled,
2315 bool& isAutoScrollEnabled,
2316 bool isAutoScrollMaxTextureExceeded,
2317 bool isHiddenInputEnabled,
2318 DevelText::EllipsisPosition::Type ellipsisPosition)
2320 return mImpl->LayoutText(layoutParameters,
2323 isAutoScrollEnabled,
2324 isAutoScrollMaxTextureExceeded,
2325 isHiddenInputEnabled,
2329 void Engine::Align(const Size& size,
2330 CharacterIndex startIndex,
2331 Length numberOfCharacters,
2332 Text::HorizontalAlignment::Type horizontalAlignment,
2333 Vector<LineRun>& lines,
2334 float& alignmentOffset,
2335 Dali::LayoutDirection::Type layoutDirection,
2336 bool matchLayoutDirection)
2341 horizontalAlignment,
2345 matchLayoutDirection);
2348 void Engine::SetDefaultLineSpacing(float lineSpacing)
2350 mImpl->mDefaultLineSpacing = lineSpacing;
2353 float Engine::GetDefaultLineSpacing() const
2355 return mImpl->mDefaultLineSpacing;
2358 void Engine::SetDefaultLineSize(float lineSize)
2360 mImpl->mDefaultLineSize = lineSize;
2363 float Engine::GetDefaultLineSize() const
2365 return mImpl->mDefaultLineSize;
2368 void Engine::SetRelativeLineSize(float relativeLineSize)
2370 mImpl->mRelativeLineSize = relativeLineSize;
2373 float Engine::GetRelativeLineSize() const
2375 return mImpl->mRelativeLineSize;
2378 } // namespace Layout
2382 } // namespace Toolkit