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>
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;
452 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
453 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
458 // Continue traversing in the first half of line or in the whole line.
459 // If the second half of line was extended then continue from logical index in the first half of line
460 // Also this is valid when the line was not splitted and there were WhiteSpace.
461 // Otherwise start from first logical index in line.
462 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
463 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
465 // Convert the character in the logical order into the character in the visual order.
466 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
467 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
469 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
471 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
472 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
473 lastGlyphOfParagraphPlusOne,
474 charactersPerGlyphBuffer);
476 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
478 GlyphMetrics glyphMetrics;
479 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
480 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
481 GetGlyphsMetrics(glyphIndex,
482 numberOfGLyphsInGroup,
490 // If glyph is WhiteSpace then:
491 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
492 // the endOfLine in RTL was the headOfLine for layouting.
493 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
494 if(RTL == bidirectionalLineInfo.direction)
496 length += glyphMetrics.advance;
500 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
502 penX += glyphMetrics.advance;
506 // If glyph is not whiteSpace then:
507 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
508 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
509 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
510 // Otherwise the current length is maximum.
511 if(LTR == bidirectionalLineInfo.direction)
513 whiteSpaceLengthEndOfLine = 0.f;
515 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
516 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
521 void ReorderBiDiLayout(const Parameters& parameters,
522 LayoutBidiParameters& bidiParameters,
523 const LineLayout& currentLineLayout,
524 LineLayout& lineLayout,
525 bool breakInCharacters,
526 bool enforceEllipsisInSingleLine)
528 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
530 // The last glyph to be laid-out.
531 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
533 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
535 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
536 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
537 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
539 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
541 // Sets the visual to logical map tables needed to reorder the text.
542 ReorderLine(bidirectionalParagraphInfo,
543 bidirectionalLinesInfo,
544 bidiParameters.bidiLineIndex,
545 lineLayout.characterIndex,
546 lineLayout.numberOfCharacters,
547 lineLayout.characterIndexInSecondHalfLine,
548 lineLayout.numberOfCharactersInSecondHalfLine,
549 bidiParameters.paragraphDirection);
551 // Recalculate the length of the line and update the layout.
552 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
554 if(!bidirectionalLineInfo.isIdentity)
557 float whiteSpaceLengthEndOfLine = 0.f;
558 LayoutRightToLeft(parameters,
559 bidirectionalLineInfo,
561 whiteSpaceLengthEndOfLine);
563 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
564 if(!Equals(length, lineLayout.length))
566 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
568 if(isMultiline && (length > parameters.boundingBox.width))
570 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
572 // The word doesn't fit in one line. It has to be split by character.
574 // Remove the last laid out glyph(s) as they doesn't fit.
575 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
577 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
578 lastGlyphOfParagraphPlusOne,
579 charactersPerGlyphBuffer);
581 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
583 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
584 lineLayout.numberOfCharacters -= numberOfCharacters;
586 AdjustLayout(parameters,
588 bidirectionalParagraphInfo,
591 if(lineLayout.length < parameters.boundingBox.width)
596 if(glyphIndex < numberOfGLyphsInGroup)
598 // avoids go under zero for an unsigned int.
602 glyphIndex -= numberOfGLyphsInGroup;
607 lineLayout = currentLineLayout;
609 AdjustLayout(parameters,
611 bidirectionalParagraphInfo,
617 lineLayout.length = std::max(length, lineLayout.length);
624 void AdjustLayout(const Parameters& parameters,
625 LayoutBidiParameters& bidiParameters,
626 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
627 LineLayout& lineLayout)
629 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
631 // Remove current reordered line.
632 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
634 // Re-build the conversion table without the removed glyphs.
635 ReorderLine(bidirectionalParagraphInfo,
636 bidirectionalLinesInfo,
637 bidiParameters.bidiLineIndex,
638 lineLayout.characterIndex,
639 lineLayout.numberOfCharacters,
640 lineLayout.characterIndexInSecondHalfLine,
641 lineLayout.numberOfCharactersInSecondHalfLine,
642 bidiParameters.paragraphDirection);
644 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
647 float whiteSpaceLengthEndOfLine = 0.f;
648 LayoutRightToLeft(parameters,
649 bidirectionalLineInfo,
651 whiteSpaceLengthEndOfLine);
653 lineLayout.length = length;
654 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
658 * Retrieves the line layout for a given box width.
660 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
661 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
662 * to reorder the line and recalculate its length.
665 * @param[in] parameters The layout parameters.
666 * @param[] bidiParameters Bidirectional info for the current line.
667 * @param[out] lineLayout The line layout.
668 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
669 * @param[in] ellipsisPosition Where is the location the text elide
671 void GetLineLayoutForBox(const Parameters& parameters,
672 LayoutBidiParameters& bidiParameters,
673 LineLayout& lineLayout,
675 DevelText::EllipsisPosition::Type ellipsisPosition,
676 bool enforceEllipsisInSingleLine,
677 bool elideTextEnabled)
679 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
680 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
682 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
683 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
684 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
685 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
686 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
688 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
689 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
691 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
692 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
693 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
694 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
695 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
696 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
698 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
700 // The last glyph to be laid-out.
701 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
703 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
704 // In the case the line starts with a right to left character, if the width is longer than the advance,
705 // the difference needs to be added to the line length.
707 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
708 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
709 lastGlyphOfParagraphPlusOne,
710 charactersPerGlyphBuffer);
712 float targetWidth = parameters.boundingBox.width;
713 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
715 bool isSecondHalf = false;
717 const float modelCharacterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
718 float calculatedAdvance = 0.f;
719 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
720 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
722 // Get the character-spacing runs.
723 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
725 GlyphMetrics glyphMetrics;
726 const float characterSpacing = GetGlyphCharacterSpacing(lineLayout.glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
727 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
728 GetGlyphsMetrics(lineLayout.glyphIndex,
729 numberOfGLyphsInGroup,
735 // Set the direction of the first character of the line.
736 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
738 // Stores temporary line layout which has not been added to the final line layout.
739 LineLayout tmpLineLayout;
741 // Initialize the start point.
743 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
744 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
745 // 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.
746 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
748 tmpLineLayout.relativeLineSize = lineLayout.relativeLineSize;
750 // Calculate the line height if there is no characters.
751 FontId lastFontId = glyphMetrics.fontId;
752 UpdateLineHeight(glyphMetrics, tmpLineLayout);
754 bool oneWordLaidOut = false;
755 bool oneHyphenLaidOut = false;
756 GlyphIndex hyphenIndex = 0;
757 GlyphInfo hyphenGlyph;
759 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
760 glyphIndex < lastGlyphOfParagraphPlusOne;)
762 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
764 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
765 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
766 lastGlyphOfParagraphPlusOne,
767 charactersPerGlyphBuffer);
769 GlyphMetrics glyphMetrics;
770 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
771 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
772 GetGlyphsMetrics(glyphIndex,
773 numberOfGLyphsInGroup,
779 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
781 // Check if the font of the current glyph is the same of the previous one.
782 // If it's different the ascender and descender need to be updated.
783 if(lastFontId != glyphMetrics.fontId)
785 UpdateLineHeight(glyphMetrics, tmpLineLayout);
786 lastFontId = glyphMetrics.fontId;
789 // Get the character indices for the current glyph. The last character index is needed
790 // because there are glyphs formed by more than one character but their break info is
791 // given only for the last character.
792 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
793 const bool hasCharacters = charactersPerGlyph > 0u;
794 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
795 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
797 // Get the line break info for the current character.
798 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
802 // Increase the number of characters.
803 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
805 // Increase the number of glyphs.
806 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
810 // Increase the number of characters.
811 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
813 // Increase the number of glyphs.
814 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
817 // Check whether is a white space.
818 const Character character = *(textBuffer + characterFirstIndex);
819 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
821 // Calculate the length of the line.
823 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
824 const float previousTmpPenX = tmpLineLayout.penX;
825 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
826 const float previousTmpLength = tmpLineLayout.length;
827 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
831 // Add the length to the length of white spaces at the end of the line.
832 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
833 // The advance is used as the width is always zero for the white spaces.
837 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
838 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
840 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
842 // Clear the white space length at the end of the line.
843 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
846 if(isSplitToTwoHalves && (!isSecondHalf) &&
847 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
849 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
850 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
852 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
853 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
855 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
856 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
858 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
860 // Check if the accumulated length fits in the width of the box.
861 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
862 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
863 completelyFill && !isMultiline &&
864 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
866 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
868 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
870 GlyphMetrics glyphMetrics;
871 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndexToRemove, characterSpacingGlyphRuns, modelCharacterSpacing);
872 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
873 GetGlyphsMetrics(glyphIndexToRemove,
874 numberOfGLyphsInGroup,
880 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
881 lastGlyphOfParagraphPlusOne,
882 charactersPerGlyphBuffer);
884 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
885 const bool hasCharacters = charactersPerGlyph > 0u;
886 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
887 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
889 // Check whether is a white space.
890 const Character character = *(textBuffer + characterFirstIndex);
891 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
895 // Decrease the number of characters for SecondHalf.
896 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
898 // Decrease the number of glyphs for SecondHalf.
899 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
903 // Decrease the number of characters.
904 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
906 // Decrease the number of glyphs.
907 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
910 if(isRemovedGlyphWhiteSpace)
912 tmpLineLayout.penX -= glyphMetrics.advance;
913 tmpLineLayout.length -= glyphMetrics.advance;
917 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
918 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
923 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
924 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
925 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
929 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
930 tmpLineLayout.characterIndex = characterLastIndex + 1u;
931 glyphIndexToRemove = tmpLineLayout.glyphIndex;
935 else if((completelyFill || isMultiline) &&
936 (tmpLineLayout.length > targetWidth))
938 // Current word does not fit in the box's width.
939 if(((oneHyphenLaidOut && isHyphenMode) ||
940 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
943 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
944 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
947 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
949 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
951 // The word doesn't fit in the control's width. It needs to be split by character.
952 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
956 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
957 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
961 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
962 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
965 tmpLineLayout.penX = previousTmpPenX;
966 tmpLineLayout.previousAdvance = previousTmpAdvance;
967 tmpLineLayout.length = previousTmpLength;
968 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
971 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
973 // Add part of the word to the line layout and shift the first glyph.
974 MergeLineLayout(lineLayout, tmpLineLayout, true);
976 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
977 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
979 // Add part of the word to the line layout.
980 MergeLineLayout(lineLayout, tmpLineLayout, false);
985 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
988 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
990 // Reorder the RTL line.
991 if(bidiParameters.isBidirectional)
993 ReorderBiDiLayout(parameters,
998 enforceEllipsisInSingleLine);
1004 if((isMultiline || isLastGlyph) &&
1005 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
1007 LineLayout currentLineLayout = lineLayout;
1008 oneHyphenLaidOut = false;
1010 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1012 // Must break the line. Update the line layout, shift the first glyph and return.
1013 MergeLineLayout(lineLayout, tmpLineLayout, true);
1017 // Must break the line. Update the line layout and return.
1018 MergeLineLayout(lineLayout, tmpLineLayout, false);
1021 // Reorder the RTL line.
1022 if(bidiParameters.isBidirectional)
1024 ReorderBiDiLayout(parameters,
1029 enforceEllipsisInSingleLine);
1032 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
1033 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1039 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1041 oneHyphenLaidOut = false;
1042 oneWordLaidOut = isWordLaidOut;
1043 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
1045 // Current glyph is the last one of the current word.
1046 // Add the temporal layout to the current one.
1047 MergeLineLayout(lineLayout, tmpLineLayout, false);
1049 tmpLineLayout.Clear();
1053 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1054 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1056 hyphenGlyph = GlyphInfo();
1057 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1059 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1060 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1062 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1064 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1066 hyphenIndex = glyphIndex;
1067 oneHyphenLaidOut = true;
1069 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1071 // Current glyph is the last one of the current word hyphen.
1072 // Add the temporal layout to the current one.
1073 MergeLineLayout(lineLayout, tmpLineLayout, false);
1075 tmpLineLayout.Clear();
1079 glyphIndex += numberOfGLyphsInGroup;
1082 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1085 void SetGlyphPositions(const Parameters& layoutParameters,
1086 Vector2* glyphPositionsBuffer,
1087 const LineLayout& layout)
1089 // Traverse the glyphs and set the positions.
1091 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1092 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1093 const Length numberOfGlyphs = layout.numberOfGlyphs;
1094 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1096 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1097 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1099 // Check if the x bearing of the first character is negative.
1100 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1101 // so the penX position needs to be moved to the right.
1102 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1103 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1105 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1106 layoutParameters.textModel->mLogicalModel,
1107 interGlyphExtraAdvance,
1109 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1110 startIndexForGlyphPositions,
1111 glyphPositionsBuffer,
1114 if(layout.isSplitToTwoHalves)
1116 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1117 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1118 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1120 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1121 layoutParameters.textModel->mLogicalModel,
1122 interGlyphExtraAdvance,
1123 numberOfGlyphsInSecondHalfLine,
1124 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1125 startIndexForGlyphPositionsnSecondHalf,
1126 glyphPositionsBuffer,
1131 void SetGlyphPositions(const Parameters& layoutParameters,
1132 Vector2* glyphPositionsBuffer,
1133 LayoutBidiParameters& layoutBidiParameters,
1134 const LineLayout& layout)
1136 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1137 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1138 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1140 CharacterIndex characterLogicalIndex = 0u;
1141 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1142 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1146 if(layout.isSplitToTwoHalves)
1148 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1149 layoutParameters.textModel->mLogicalModel,
1150 layoutBidiParameters.bidiLineIndex,
1151 layoutParameters.startGlyphIndex,
1152 glyphPositionsBuffer,
1153 characterVisualIndex,
1154 characterLogicalIndex,
1158 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1160 extendedToSecondHalf = true;
1161 characterLogicalIndex = 0u;
1162 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1164 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1165 layoutParameters.textModel->mLogicalModel,
1166 layoutBidiParameters.bidiLineIndex,
1167 layoutParameters.startGlyphIndex,
1168 glyphPositionsBuffer,
1169 characterVisualIndex,
1170 characterLogicalIndex,
1174 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1175 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1177 penX += -glyph.xBearing;
1179 // Traverses the characters of the right to left paragraph.
1180 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1182 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1183 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1184 layoutParameters.startGlyphIndex,
1185 layoutParameters.interGlyphExtraAdvance,
1186 bidiLine.characterRunForSecondHalfLine,
1187 bidiLine.visualToLogicalMapSecondHalf,
1188 glyphPositionsBuffer,
1189 characterLogicalIndex,
1193 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1195 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1196 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1197 layoutParameters.startGlyphIndex,
1198 layoutParameters.interGlyphExtraAdvance,
1199 bidiLine.characterRun,
1200 bidiLine.visualToLogicalMap,
1201 glyphPositionsBuffer,
1202 characterLogicalIndex,
1207 * @brief Resizes the line buffer.
1209 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1210 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1211 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1212 * @param[in] updateCurrentBuffer Whether the layout is updated.
1214 * @return Pointer to either lines or newLines.
1216 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1217 Vector<LineRun>& newLines,
1218 Length& linesCapacity,
1219 bool updateCurrentBuffer)
1221 LineRun* linesBuffer = nullptr;
1222 // Reserve more space for the next lines.
1223 linesCapacity *= 2u;
1224 if(updateCurrentBuffer)
1226 newLines.Resize(linesCapacity);
1227 linesBuffer = newLines.Begin();
1231 lines.Resize(linesCapacity);
1232 linesBuffer = lines.Begin();
1239 * Ellipsis a line if it exceeds the width's of the bounding box.
1241 * @param[in] layoutParameters The parameters needed to layout the text.
1242 * @param[in] layout The line layout.
1243 * @param[in,out] layoutSize The text's layout size.
1244 * @param[in,out] linesBuffer Pointer to the line's buffer.
1245 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1246 * @param[in,out] numberOfLines The number of laid-out lines.
1247 * @param[in] penY The vertical layout position.
1248 * @param[in] currentParagraphDirection The current paragraph's direction.
1249 * @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
1250 * @param[in] ellipsisPosition Where is the location the text elide
1252 * return Whether the line is ellipsized.
1254 bool EllipsisLine(const Parameters& layoutParameters,
1255 LayoutBidiParameters& layoutBidiParameters,
1256 const LineLayout& layout,
1258 LineRun* linesBuffer,
1259 Vector2* glyphPositionsBuffer,
1260 Length& numberOfLines,
1262 bool& isAutoScrollEnabled,
1263 bool isAutoScrollMaxTextureExceeded,
1264 DevelText::EllipsisPosition::Type ellipsisPosition,
1265 bool enforceEllipsisInSingleLine)
1267 const bool ellipsis = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? isAutoScrollMaxTextureExceeded : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
1268 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1269 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1271 if(penY - layout.descender > layoutParameters.boundingBox.height)
1273 // Even if auto scroll is enabled and text is bigger than max texture size,
1274 // if the the height is small, auto scroll should not work.
1275 isAutoScrollEnabled = false;
1278 // Do not layout more lines if ellipsis is enabled.
1279 // The last line needs to be completely filled with characters.
1280 // Part of a word may be used.
1282 LineRun* lineRun = nullptr;
1283 LineLayout ellipsisLayout;
1285 ellipsisLayout.relativeLineSize = layout.relativeLineSize;
1287 if(0u != numberOfLines)
1289 // Get the last line and layout it again with the 'completelyFill' flag to true.
1290 lineRun = linesBuffer + (numberOfLines - 1u);
1291 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1293 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1297 // At least there is space reserved for one line.
1298 lineRun = linesBuffer;
1300 lineRun->glyphRun.glyphIndex = 0u;
1301 ellipsisLayout.glyphIndex = 0u;
1302 lineRun->isSplitToTwoHalves = false;
1307 GetLineLayoutForBox(layoutParameters,
1308 layoutBidiParameters,
1312 enforceEllipsisInSingleLine,
1315 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1317 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1320 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1321 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1322 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1323 lineRun->width = ellipsisLayout.length;
1324 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1325 lineRun->ascender = ellipsisLayout.ascender;
1326 lineRun->descender = ellipsisLayout.descender;
1327 lineRun->ellipsis = true;
1329 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1330 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1331 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1332 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1333 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1335 layoutSize.width = layoutParameters.boundingBox.width;
1336 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1338 layoutSize.height += GetLineHeight(*lineRun, true);
1342 //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1343 //usually we don't includ it in normal cases using GetLineHeight()
1344 if(lineRun->lineSpacing < 0)
1346 layoutSize.height -= lineRun->lineSpacing;
1350 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1352 if(layoutBidiParameters.isBidirectional)
1354 layoutBidiParameters.bidiLineIndex = 0u;
1355 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1356 endIt = bidirectionalLinesInfo.End();
1358 ++it, ++layoutBidiParameters.bidiLineIndex)
1360 const BidirectionalLineInfoRun& run = *it;
1361 //To handle case when the laid characters exist in next line.
1362 //More than one BidirectionalLineInfoRun could start with same character.
1363 //When need to check also numberOfCharacters in line.
1364 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1365 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1366 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1367 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1368 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1370 // Found where to insert the bidi line info.
1376 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1378 if((nullptr != bidirectionalLineInfo) &&
1379 !bidirectionalLineInfo->isIdentity &&
1380 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1382 lineRun->direction = RTL;
1383 SetGlyphPositions(layoutParameters,
1384 glyphPositionsBuffer,
1385 layoutBidiParameters,
1390 lineRun->direction = LTR;
1391 SetGlyphPositions(layoutParameters,
1392 glyphPositionsBuffer,
1401 * @brief Updates the text layout with a new laid-out line.
1403 * @param[in] layoutParameters The parameters needed to layout the text.
1404 * @param[in] layout The line layout.
1405 * @param[in,out] layoutSize The text's layout size.
1406 * @param[in,out] linesBuffer Pointer to the line's buffer.
1407 * @param[in] index Index to the vector of glyphs.
1408 * @param[in,out] numberOfLines The number of laid-out lines.
1409 * @param[in] isLastLine Whether the laid-out line is the last one.
1411 void UpdateTextLayout(const Parameters& layoutParameters,
1412 const LineLayout& layout,
1414 LineRun* linesBuffer,
1416 Length& numberOfLines,
1419 LineRun& lineRun = *(linesBuffer + numberOfLines);
1422 lineRun.glyphRun.glyphIndex = index;
1423 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1424 lineRun.characterRun.characterIndex = layout.characterIndex;
1425 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1426 lineRun.width = layout.length;
1427 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1429 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1430 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1431 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1432 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1433 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1435 // Rounds upward to avoid a non integer size.
1436 lineRun.width = std::ceil(lineRun.width);
1438 lineRun.ascender = layout.ascender;
1439 lineRun.descender = layout.descender;
1440 lineRun.direction = layout.direction;
1441 lineRun.ellipsis = false;
1443 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
1445 // Update the actual size.
1446 if(lineRun.width > layoutSize.width)
1448 layoutSize.width = lineRun.width;
1451 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1455 * @brief Updates the text layout with the last laid-out line.
1457 * @param[in] layoutParameters The parameters needed to layout the text.
1458 * @param[in] characterIndex The character index of the line.
1459 * @param[in] glyphIndex The glyph index of the line.
1460 * @param[in,out] layoutSize The text's layout size.
1461 * @param[in,out] linesBuffer Pointer to the line's buffer.
1462 * @param[in,out] numberOfLines The number of laid-out lines.
1464 void UpdateTextLayout(const Parameters& layoutParameters,
1465 CharacterIndex characterIndex,
1466 GlyphIndex glyphIndex,
1468 LineRun* linesBuffer,
1469 Length& numberOfLines)
1471 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1473 // Need to add a new line with no characters but with height to increase the layoutSize.height
1474 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1476 Text::FontMetrics fontMetrics;
1477 if(0u != glyphInfo.fontId)
1479 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1482 LineRun& lineRun = *(linesBuffer + numberOfLines);
1485 lineRun.glyphRun.glyphIndex = glyphIndex;
1486 lineRun.glyphRun.numberOfGlyphs = 0u;
1487 lineRun.characterRun.characterIndex = characterIndex;
1488 lineRun.characterRun.numberOfCharacters = 0u;
1489 lineRun.width = 0.f;
1490 lineRun.ascender = fontMetrics.ascender;
1491 lineRun.descender = fontMetrics.descender;
1492 lineRun.extraLength = 0.f;
1493 lineRun.alignmentOffset = 0.f;
1494 lineRun.direction = LTR;
1495 lineRun.ellipsis = false;
1497 BoundedParagraphRun currentParagraphRun;
1498 LineLayout tempLineLayout;
1499 (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
1501 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
1503 layoutSize.height += GetLineHeight(lineRun, true);
1507 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1509 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1510 * @param[in,out] layoutSize The text's layout size.
1512 void UpdateLayoutSize(const Vector<LineRun>& lines,
1515 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1516 endIt = lines.End();
1520 const LineRun& line = *it;
1521 bool isLastLine = (it + 1 == endIt);
1523 if(line.width > layoutSize.width)
1525 layoutSize.width = line.width;
1528 layoutSize.height += GetLineHeight(line, isLastLine);
1533 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1535 * @param[in] layoutParameters The parameters needed to layout the text.
1536 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1537 * @param[in] characterOffset The offset to be added to the runs of characters.
1538 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1540 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1541 Vector<LineRun>& lines,
1542 Length characterOffset,
1545 // Update the glyph and character runs.
1546 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1547 endIt = lines.End();
1551 LineRun& line = *it;
1553 line.glyphRun.glyphIndex = glyphOffset;
1554 line.characterRun.characterIndex = characterOffset;
1556 glyphOffset += line.glyphRun.numberOfGlyphs;
1557 characterOffset += line.characterRun.numberOfCharacters;
1562 * @brief Sets the relative line size for the LineLayout
1564 * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
1565 * @param[in,out] lineLayout The line layout to be updated.
1567 void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
1569 lineLayout.relativeLineSize = mRelativeLineSize;
1571 if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
1573 lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
1578 * @brief Get the bounded paragraph for the characterIndex if exists.
1580 * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
1581 * @param[in] characterIndex The character index to get bounded paragraph for.
1582 * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
1584 * @return returns true if a bounded paragraph was found.
1586 bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
1588 for(Vector<BoundedParagraphRun>::Iterator it = boundedParagraphRuns.Begin(),
1589 endIt = boundedParagraphRuns.End();
1593 BoundedParagraphRun& tempParagraphRun = *it;
1595 if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
1596 characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
1598 currentParagraphRun = tempParagraphRun;
1606 bool LayoutText(Parameters& layoutParameters,
1608 bool elideTextEnabled,
1609 bool& isAutoScrollEnabled,
1610 bool isAutoScrollMaxTextureExceeded,
1611 DevelText::EllipsisPosition::Type ellipsisPosition)
1613 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1614 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1616 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1617 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1619 //Reset indices of ElidedGlyphs
1620 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1621 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1622 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1623 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1625 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1626 const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
1628 if(0u == layoutParameters.numberOfGlyphs)
1630 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1631 if(layoutParameters.isLastNewParagraph)
1633 Length numberOfLines = lines.Count();
1634 if(0u != numberOfLines)
1636 const LineRun& lastLine = *(lines.End() - 1u);
1638 if(0u != lastLine.characterRun.numberOfCharacters)
1640 // Need to add a new line with no characters but with height to increase the layoutSize.height
1642 Initialize(newLine);
1643 lines.PushBack(newLine);
1645 UpdateTextLayout(layoutParameters,
1646 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1647 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1655 // Calculates the layout size.
1656 UpdateLayoutSize(lines,
1659 // Rounds upward to avoid a non integer size.
1660 layoutSize.height = std::ceil(layoutSize.height);
1662 // Nothing else do if there are no glyphs to layout.
1666 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1667 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1668 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1670 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1671 // This extra line needs to be removed.
1672 if(0u != lines.Count())
1674 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1676 if((0u == lastLine->characterRun.numberOfCharacters) &&
1677 (lastGlyphPlusOne == totalNumberOfGlyphs))
1679 lines.Remove(lastLine);
1683 // Retrieve BiDi info.
1684 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1686 const CharacterIndex* const glyphsToCharactersBuffer = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
1687 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1688 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1690 // Set the layout bidirectional paramters.
1691 LayoutBidiParameters layoutBidiParameters;
1693 // Whether the layout is being updated or set from scratch.
1694 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1696 Vector2* glyphPositionsBuffer = nullptr;
1697 Vector<Vector2> newGlyphPositions;
1699 LineRun* linesBuffer = nullptr;
1700 Vector<LineRun> newLines;
1702 // Estimate the number of lines.
1703 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1704 Length numberOfLines = 0u;
1706 if(updateCurrentBuffer)
1708 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1709 glyphPositionsBuffer = newGlyphPositions.Begin();
1711 newLines.Resize(linesCapacity);
1712 linesBuffer = newLines.Begin();
1716 glyphPositionsBuffer = glyphPositions.Begin();
1718 lines.Resize(linesCapacity);
1719 linesBuffer = lines.Begin();
1722 float penY = CalculateLineOffset(lines,
1723 layoutParameters.startLineIndex);
1724 bool anyLineIsEliped = false;
1725 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1727 layoutBidiParameters.Clear();
1729 if(hasBidiParagraphs)
1731 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1733 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1734 endIt = bidirectionalParagraphsInfo.End();
1736 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1738 const BidirectionalParagraphInfoRun& run = *it;
1740 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1742 if(lastCharacterIndex <= startCharacterIndex)
1744 // Do not process, the paragraph has already been processed.
1748 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1750 layoutBidiParameters.paragraphDirection = run.direction;
1751 layoutBidiParameters.isBidirectional = true;
1754 // Has already been found.
1758 if(layoutBidiParameters.isBidirectional)
1760 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1761 endIt = bidirectionalLinesInfo.End();
1763 ++it, ++layoutBidiParameters.bidiLineIndex)
1765 const BidirectionalLineInfoRun& run = *it;
1767 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1769 if(lastCharacterIndex <= startCharacterIndex)
1775 if(startCharacterIndex < lastCharacterIndex)
1777 // Found where to insert the bidi line info.
1784 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1786 // Get the layout for the line.
1788 layout.direction = layoutBidiParameters.paragraphDirection;
1789 layout.glyphIndex = index;
1791 BoundedParagraphRun currentParagraphRun;
1792 (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
1794 GetLineLayoutForBox(layoutParameters,
1795 layoutBidiParameters,
1802 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1803 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1804 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1805 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1806 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1808 CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
1810 //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)
1811 if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex + layout.numberOfCharacters)
1813 layout.relativeLineSize = mRelativeLineSize;
1816 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1818 // The width is too small and no characters are laid-out.
1819 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1821 lines.Resize(numberOfLines);
1823 // Rounds upward to avoid a non integer size.
1824 layoutSize.height = std::ceil(layoutSize.height);
1829 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1831 penY += layout.ascender;
1833 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1835 bool ellipsis = false;
1836 if(elideTextEnabled)
1838 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1840 // Does the ellipsis of the last line.
1841 ellipsis = EllipsisLine(layoutParameters,
1842 layoutBidiParameters,
1846 glyphPositionsBuffer,
1849 isAutoScrollEnabled,
1850 isAutoScrollMaxTextureExceeded,
1855 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1857 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1858 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1860 ellipsis = EllipsisLine(layoutParameters,
1861 layoutBidiParameters,
1865 glyphPositionsBuffer,
1868 isAutoScrollEnabled,
1869 isAutoScrollMaxTextureExceeded,
1874 //clear hyphen from ellipsis line
1875 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1876 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1878 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1880 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1881 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1885 // No more lines to layout.
1890 //In START location of ellipsis whether to shift lines or not.
1891 anyLineIsEliped |= ellipsis;
1893 // Whether the last line has been laid-out.
1894 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1896 if(numberOfLines == linesCapacity)
1898 // Reserve more space for the next lines.
1899 linesBuffer = ResizeLinesBuffer(lines,
1902 updateCurrentBuffer);
1905 // Updates the current text's layout with the line's layout.
1906 UpdateTextLayout(layoutParameters,
1914 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1916 if((nextIndex == totalNumberOfGlyphs) &&
1917 layoutParameters.isLastNewParagraph &&
1918 (mLayout == MULTI_LINE_BOX))
1920 // The last character of the text is a new paragraph character.
1921 // An extra line with no characters is added to increase the text's height
1922 // in order to place the cursor.
1924 if(numberOfLines == linesCapacity)
1926 // Reserve more space for the next lines.
1927 linesBuffer = ResizeLinesBuffer(lines,
1930 updateCurrentBuffer);
1933 UpdateTextLayout(layoutParameters,
1934 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1935 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1939 } // whether to add a last line.
1941 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1943 if((nullptr != bidirectionalLineInfo) &&
1944 !bidirectionalLineInfo->isIdentity &&
1945 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1947 SetGlyphPositions(layoutParameters,
1948 glyphPositionsBuffer,
1949 layoutBidiParameters,
1954 // Sets the positions of the glyphs.
1955 SetGlyphPositions(layoutParameters,
1956 glyphPositionsBuffer,
1960 // Updates the vertical pen's position.
1961 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
1963 // Increase the glyph index.
1966 } // end for() traversing glyphs.
1968 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1969 if(anyLineIsEliped && numberOfLines > 1u)
1971 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1973 Length lineIndex = 0;
1974 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1976 LineRun& delLine = linesBuffer[lineIndex];
1977 delLine.ellipsis = true;
1979 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1980 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1982 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1983 linesBuffer[lineIndex].ellipsis = false;
1987 linesBuffer[0u].ellipsis = true;
1989 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1991 Length middleLineIndex = (numberOfLines) / 2u;
1992 Length ellipsisLineIndex = 0u;
1993 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1995 LineRun& delLine = linesBuffer[middleLineIndex];
1996 delLine.ellipsis = true;
1998 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1999 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
2001 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
2002 linesBuffer[lineIndex].ellipsis = false;
2005 ellipsisLineIndex = middleLineIndex - 1u;
2006 middleLineIndex = (numberOfLines) / 2u;
2009 linesBuffer[ellipsisLineIndex].ellipsis = true;
2013 if(updateCurrentBuffer)
2015 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
2016 newGlyphPositions.Begin(),
2017 newGlyphPositions.End());
2018 glyphPositions.Resize(totalNumberOfGlyphs);
2020 newLines.Resize(numberOfLines);
2022 // Current text's layout size adds only the newly laid-out lines.
2023 // Updates the layout size with the previously laid-out lines.
2024 UpdateLayoutSize(lines,
2027 if(0u != newLines.Count())
2029 const LineRun& lastLine = *(newLines.End() - 1u);
2031 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
2032 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
2034 // Update the indices of the runs before the new laid-out lines are inserted.
2035 UpdateLineIndexOffsets(layoutParameters,
2040 // Insert the lines.
2041 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
2048 lines.Resize(numberOfLines);
2051 // Rounds upward to avoid a non integer size.
2052 layoutSize.height = std::ceil(layoutSize.height);
2054 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
2059 void Align(const Size& size,
2060 CharacterIndex startIndex,
2061 Length numberOfCharacters,
2062 Text::HorizontalAlignment::Type horizontalAlignment,
2063 Vector<LineRun>& lines,
2064 float& alignmentOffset,
2065 Dali::LayoutDirection::Type layoutDirection,
2066 bool matchLayoutDirection)
2068 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
2070 alignmentOffset = MAX_FLOAT;
2071 // Traverse all lines and align the glyphs.
2072 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
2076 LineRun& line = *it;
2078 if(line.characterRun.characterIndex < startIndex)
2080 // Do not align lines which have already been aligned.
2084 if(line.characterRun.characterIndex > lastCharacterPlusOne)
2086 // Do not align lines beyond the last laid-out character.
2090 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
2092 // Do not align lines beyond the last laid-out character unless the line is last and empty.
2096 // Calculate the line's alignment offset accordingly with the align option,
2097 // the box width, line length, and the paragraph's direction.
2098 CalculateHorizontalAlignment(size.width,
2099 horizontalAlignment,
2102 matchLayoutDirection);
2104 // Updates the alignment offset.
2105 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2109 void CalculateHorizontalAlignment(float boxWidth,
2110 HorizontalAlignment::Type horizontalAlignment,
2112 Dali::LayoutDirection::Type layoutDirection,
2113 bool matchLayoutDirection)
2115 line.alignmentOffset = 0.f;
2116 const bool isLineRTL = RTL == line.direction;
2118 // Whether to swap the alignment.
2119 // 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.
2120 bool isLayoutRTL = isLineRTL;
2121 float lineLength = line.width;
2123 // match align for system language direction
2124 if(matchLayoutDirection)
2126 // Swap the alignment type if the line is right to left.
2127 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2129 // Calculate the horizontal line offset.
2130 switch(horizontalAlignment)
2132 case HorizontalAlignment::BEGIN:
2138 lineLength += line.extraLength;
2141 line.alignmentOffset = boxWidth - lineLength;
2145 line.alignmentOffset = 0.f;
2149 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2150 line.alignmentOffset -= line.extraLength;
2155 case HorizontalAlignment::CENTER:
2157 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2161 line.alignmentOffset -= line.extraLength;
2164 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2167 case HorizontalAlignment::END:
2171 line.alignmentOffset = 0.f;
2175 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2176 line.alignmentOffset -= line.extraLength;
2183 lineLength += line.extraLength;
2186 line.alignmentOffset = boxWidth - lineLength;
2193 void Initialize(LineRun& line)
2195 line.glyphRun.glyphIndex = 0u;
2196 line.glyphRun.numberOfGlyphs = 0u;
2197 line.characterRun.characterIndex = 0u;
2198 line.characterRun.numberOfCharacters = 0u;
2200 line.ascender = 0.f;
2201 line.descender = 0.f;
2202 line.extraLength = 0.f;
2203 line.alignmentOffset = 0.f;
2204 line.direction = LTR;
2205 line.ellipsis = false;
2206 line.lineSpacing = mDefaultLineSpacing;
2207 line.isSplitToTwoHalves = false;
2208 line.glyphRunSecondHalf.glyphIndex = 0u;
2209 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2210 line.characterRunForSecondHalfLine.characterIndex = 0u;
2211 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2216 float mDefaultLineSpacing;
2217 float mDefaultLineSize;
2219 IntrusivePtr<Metrics> mMetrics;
2220 float mRelativeLineSize;
2226 mImpl = new Engine::Impl();
2234 void Engine::SetMetrics(MetricsPtr& metrics)
2236 mImpl->mMetrics = metrics;
2239 void Engine::SetLayout(Type layout)
2241 mImpl->mLayout = layout;
2244 Engine::Type Engine::GetLayout() const
2246 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2247 return mImpl->mLayout;
2250 void Engine::SetCursorWidth(int width)
2252 mImpl->mCursorWidth = static_cast<float>(width);
2255 int Engine::GetCursorWidth() const
2257 return static_cast<int>(mImpl->mCursorWidth);
2260 bool Engine::LayoutText(Parameters& layoutParameters,
2262 bool elideTextEnabled,
2263 bool& isAutoScrollEnabled,
2264 bool isAutoScrollMaxTextureExceeded,
2265 DevelText::EllipsisPosition::Type ellipsisPosition)
2267 return mImpl->LayoutText(layoutParameters,
2270 isAutoScrollEnabled,
2271 isAutoScrollMaxTextureExceeded,
2275 void Engine::Align(const Size& size,
2276 CharacterIndex startIndex,
2277 Length numberOfCharacters,
2278 Text::HorizontalAlignment::Type horizontalAlignment,
2279 Vector<LineRun>& lines,
2280 float& alignmentOffset,
2281 Dali::LayoutDirection::Type layoutDirection,
2282 bool matchLayoutDirection)
2287 horizontalAlignment,
2291 matchLayoutDirection);
2294 void Engine::SetDefaultLineSpacing(float lineSpacing)
2296 mImpl->mDefaultLineSpacing = lineSpacing;
2299 float Engine::GetDefaultLineSpacing() const
2301 return mImpl->mDefaultLineSpacing;
2304 void Engine::SetDefaultLineSize(float lineSize)
2306 mImpl->mDefaultLineSize = lineSize;
2309 float Engine::GetDefaultLineSize() const
2311 return mImpl->mDefaultLineSize;
2314 void Engine::SetRelativeLineSize(float relativeLineSize)
2316 mImpl->mRelativeLineSize = relativeLineSize;
2319 float Engine::GetRelativeLineSize() const
2321 return mImpl->mRelativeLineSize;
2324 } // namespace Layout
2328 } // namespace Toolkit