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 DevelText::EllipsisPosition::Type ellipsisPosition,
1264 bool enforceEllipsisInSingleLine)
1266 const bool ellipsis = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? (penY - layout.descender > layoutParameters.boundingBox.height) : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
1267 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1268 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1270 isAutoScrollEnabled = false;
1271 // Do not layout more lines if ellipsis is enabled.
1273 // The last line needs to be completely filled with characters.
1274 // Part of a word may be used.
1276 LineRun* lineRun = nullptr;
1277 LineLayout ellipsisLayout;
1279 ellipsisLayout.relativeLineSize = layout.relativeLineSize;
1281 if(0u != numberOfLines)
1283 // Get the last line and layout it again with the 'completelyFill' flag to true.
1284 lineRun = linesBuffer + (numberOfLines - 1u);
1285 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1287 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1291 // At least there is space reserved for one line.
1292 lineRun = linesBuffer;
1294 lineRun->glyphRun.glyphIndex = 0u;
1295 ellipsisLayout.glyphIndex = 0u;
1296 lineRun->isSplitToTwoHalves = false;
1301 GetLineLayoutForBox(layoutParameters,
1302 layoutBidiParameters,
1306 enforceEllipsisInSingleLine,
1309 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1311 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1314 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1315 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1316 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1317 lineRun->width = ellipsisLayout.length;
1318 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1319 lineRun->ascender = ellipsisLayout.ascender;
1320 lineRun->descender = ellipsisLayout.descender;
1321 lineRun->ellipsis = true;
1323 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1324 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1325 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1326 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1327 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1329 layoutSize.width = layoutParameters.boundingBox.width;
1330 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1332 layoutSize.height += GetLineHeight(*lineRun, true);
1336 //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1337 //usually we don't includ it in normal cases using GetLineHeight()
1338 if(lineRun->lineSpacing < 0)
1340 layoutSize.height -= lineRun->lineSpacing;
1344 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1346 if(layoutBidiParameters.isBidirectional)
1348 layoutBidiParameters.bidiLineIndex = 0u;
1349 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1350 endIt = bidirectionalLinesInfo.End();
1352 ++it, ++layoutBidiParameters.bidiLineIndex)
1354 const BidirectionalLineInfoRun& run = *it;
1355 //To handle case when the laid characters exist in next line.
1356 //More than one BidirectionalLineInfoRun could start with same character.
1357 //When need to check also numberOfCharacters in line.
1358 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1359 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1360 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1361 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1362 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1364 // Found where to insert the bidi line info.
1370 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1372 if((nullptr != bidirectionalLineInfo) &&
1373 !bidirectionalLineInfo->isIdentity &&
1374 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1376 lineRun->direction = RTL;
1377 SetGlyphPositions(layoutParameters,
1378 glyphPositionsBuffer,
1379 layoutBidiParameters,
1384 lineRun->direction = LTR;
1385 SetGlyphPositions(layoutParameters,
1386 glyphPositionsBuffer,
1395 * @brief Updates the text layout with a new laid-out line.
1397 * @param[in] layoutParameters The parameters needed to layout the text.
1398 * @param[in] layout The line layout.
1399 * @param[in,out] layoutSize The text's layout size.
1400 * @param[in,out] linesBuffer Pointer to the line's buffer.
1401 * @param[in] index Index to the vector of glyphs.
1402 * @param[in,out] numberOfLines The number of laid-out lines.
1403 * @param[in] isLastLine Whether the laid-out line is the last one.
1405 void UpdateTextLayout(const Parameters& layoutParameters,
1406 const LineLayout& layout,
1408 LineRun* linesBuffer,
1410 Length& numberOfLines,
1413 LineRun& lineRun = *(linesBuffer + numberOfLines);
1416 lineRun.glyphRun.glyphIndex = index;
1417 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1418 lineRun.characterRun.characterIndex = layout.characterIndex;
1419 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1420 lineRun.width = layout.length;
1421 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1423 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1424 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1425 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1426 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1427 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1429 // Rounds upward to avoid a non integer size.
1430 lineRun.width = std::ceil(lineRun.width);
1432 lineRun.ascender = layout.ascender;
1433 lineRun.descender = layout.descender;
1434 lineRun.direction = layout.direction;
1435 lineRun.ellipsis = false;
1437 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
1439 // Update the actual size.
1440 if(lineRun.width > layoutSize.width)
1442 layoutSize.width = lineRun.width;
1445 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1449 * @brief Updates the text layout with the last laid-out line.
1451 * @param[in] layoutParameters The parameters needed to layout the text.
1452 * @param[in] characterIndex The character index of the line.
1453 * @param[in] glyphIndex The glyph index of the line.
1454 * @param[in,out] layoutSize The text's layout size.
1455 * @param[in,out] linesBuffer Pointer to the line's buffer.
1456 * @param[in,out] numberOfLines The number of laid-out lines.
1458 void UpdateTextLayout(const Parameters& layoutParameters,
1459 CharacterIndex characterIndex,
1460 GlyphIndex glyphIndex,
1462 LineRun* linesBuffer,
1463 Length& numberOfLines)
1465 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1467 // Need to add a new line with no characters but with height to increase the layoutSize.height
1468 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1470 Text::FontMetrics fontMetrics;
1471 if(0u != glyphInfo.fontId)
1473 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1476 LineRun& lineRun = *(linesBuffer + numberOfLines);
1479 lineRun.glyphRun.glyphIndex = glyphIndex;
1480 lineRun.glyphRun.numberOfGlyphs = 0u;
1481 lineRun.characterRun.characterIndex = characterIndex;
1482 lineRun.characterRun.numberOfCharacters = 0u;
1483 lineRun.width = 0.f;
1484 lineRun.ascender = fontMetrics.ascender;
1485 lineRun.descender = fontMetrics.descender;
1486 lineRun.extraLength = 0.f;
1487 lineRun.alignmentOffset = 0.f;
1488 lineRun.direction = LTR;
1489 lineRun.ellipsis = false;
1491 BoundedParagraphRun currentParagraphRun;
1492 LineLayout tempLineLayout;
1493 (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
1495 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
1497 layoutSize.height += GetLineHeight(lineRun, true);
1501 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1503 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1504 * @param[in,out] layoutSize The text's layout size.
1506 void UpdateLayoutSize(const Vector<LineRun>& lines,
1509 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1510 endIt = lines.End();
1514 const LineRun& line = *it;
1515 bool isLastLine = (it + 1 == endIt);
1517 if(line.width > layoutSize.width)
1519 layoutSize.width = line.width;
1522 layoutSize.height += GetLineHeight(line, isLastLine);
1527 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1529 * @param[in] layoutParameters The parameters needed to layout the text.
1530 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1531 * @param[in] characterOffset The offset to be added to the runs of characters.
1532 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1534 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1535 Vector<LineRun>& lines,
1536 Length characterOffset,
1539 // Update the glyph and character runs.
1540 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1541 endIt = lines.End();
1545 LineRun& line = *it;
1547 line.glyphRun.glyphIndex = glyphOffset;
1548 line.characterRun.characterIndex = characterOffset;
1550 glyphOffset += line.glyphRun.numberOfGlyphs;
1551 characterOffset += line.characterRun.numberOfCharacters;
1556 * @brief Sets the relative line size for the LineLayout
1558 * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
1559 * @param[in,out] lineLayout The line layout to be updated.
1561 void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
1563 lineLayout.relativeLineSize = mRelativeLineSize;
1565 if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
1567 lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
1572 * @brief Get the bounded paragraph for the characterIndex if exists.
1574 * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
1575 * @param[in] characterIndex The character index to get bounded paragraph for.
1576 * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
1578 * @return returns true if a bounded paragraph was found.
1580 bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
1582 for(Vector<BoundedParagraphRun>::Iterator it = boundedParagraphRuns.Begin(),
1583 endIt = boundedParagraphRuns.End();
1587 BoundedParagraphRun& tempParagraphRun = *it;
1589 if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
1590 characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
1592 currentParagraphRun = tempParagraphRun;
1600 bool LayoutText(Parameters& layoutParameters,
1602 bool elideTextEnabled,
1603 bool& isAutoScrollEnabled,
1604 DevelText::EllipsisPosition::Type ellipsisPosition)
1606 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1607 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1609 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1610 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1612 //Reset indices of ElidedGlyphs
1613 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1614 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1615 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1616 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1618 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1619 const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
1621 if(0u == layoutParameters.numberOfGlyphs)
1623 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1624 if(layoutParameters.isLastNewParagraph)
1626 Length numberOfLines = lines.Count();
1627 if(0u != numberOfLines)
1629 const LineRun& lastLine = *(lines.End() - 1u);
1631 if(0u != lastLine.characterRun.numberOfCharacters)
1633 // Need to add a new line with no characters but with height to increase the layoutSize.height
1635 Initialize(newLine);
1636 lines.PushBack(newLine);
1638 UpdateTextLayout(layoutParameters,
1639 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1640 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1648 // Calculates the layout size.
1649 UpdateLayoutSize(lines,
1652 // Rounds upward to avoid a non integer size.
1653 layoutSize.height = std::ceil(layoutSize.height);
1655 // Nothing else do if there are no glyphs to layout.
1659 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1660 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1661 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1663 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1664 // This extra line needs to be removed.
1665 if(0u != lines.Count())
1667 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1669 if((0u == lastLine->characterRun.numberOfCharacters) &&
1670 (lastGlyphPlusOne == totalNumberOfGlyphs))
1672 lines.Remove(lastLine);
1676 // Retrieve BiDi info.
1677 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1679 const CharacterIndex* const glyphsToCharactersBuffer = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
1680 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1681 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1683 // Set the layout bidirectional paramters.
1684 LayoutBidiParameters layoutBidiParameters;
1686 // Whether the layout is being updated or set from scratch.
1687 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1689 Vector2* glyphPositionsBuffer = nullptr;
1690 Vector<Vector2> newGlyphPositions;
1692 LineRun* linesBuffer = nullptr;
1693 Vector<LineRun> newLines;
1695 // Estimate the number of lines.
1696 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1697 Length numberOfLines = 0u;
1699 if(updateCurrentBuffer)
1701 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1702 glyphPositionsBuffer = newGlyphPositions.Begin();
1704 newLines.Resize(linesCapacity);
1705 linesBuffer = newLines.Begin();
1709 glyphPositionsBuffer = glyphPositions.Begin();
1711 lines.Resize(linesCapacity);
1712 linesBuffer = lines.Begin();
1715 float penY = CalculateLineOffset(lines,
1716 layoutParameters.startLineIndex);
1717 bool anyLineIsEliped = false;
1718 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1720 layoutBidiParameters.Clear();
1722 if(hasBidiParagraphs)
1724 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1726 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1727 endIt = bidirectionalParagraphsInfo.End();
1729 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1731 const BidirectionalParagraphInfoRun& run = *it;
1733 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1735 if(lastCharacterIndex <= startCharacterIndex)
1737 // Do not process, the paragraph has already been processed.
1741 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1743 layoutBidiParameters.paragraphDirection = run.direction;
1744 layoutBidiParameters.isBidirectional = true;
1747 // Has already been found.
1751 if(layoutBidiParameters.isBidirectional)
1753 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1754 endIt = bidirectionalLinesInfo.End();
1756 ++it, ++layoutBidiParameters.bidiLineIndex)
1758 const BidirectionalLineInfoRun& run = *it;
1760 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1762 if(lastCharacterIndex <= startCharacterIndex)
1768 if(startCharacterIndex < lastCharacterIndex)
1770 // Found where to insert the bidi line info.
1777 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1779 // Get the layout for the line.
1781 layout.direction = layoutBidiParameters.paragraphDirection;
1782 layout.glyphIndex = index;
1784 BoundedParagraphRun currentParagraphRun;
1785 (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
1787 GetLineLayoutForBox(layoutParameters,
1788 layoutBidiParameters,
1795 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1796 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1797 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1798 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1799 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1801 CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
1803 //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)
1804 if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex+layout.numberOfCharacters)
1806 layout.relativeLineSize = mRelativeLineSize;
1809 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1811 // The width is too small and no characters are laid-out.
1812 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1814 lines.Resize(numberOfLines);
1816 // Rounds upward to avoid a non integer size.
1817 layoutSize.height = std::ceil(layoutSize.height);
1822 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1824 penY += layout.ascender;
1826 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1828 bool ellipsis = false;
1829 if(elideTextEnabled)
1831 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1833 // Does the ellipsis of the last line.
1834 ellipsis = EllipsisLine(layoutParameters,
1835 layoutBidiParameters,
1839 glyphPositionsBuffer,
1842 isAutoScrollEnabled,
1847 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1849 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1850 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1852 ellipsis = EllipsisLine(layoutParameters,
1853 layoutBidiParameters,
1857 glyphPositionsBuffer,
1860 isAutoScrollEnabled,
1865 //clear hyphen from ellipsis line
1866 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1867 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1869 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1871 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1872 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1876 // No more lines to layout.
1881 //In START location of ellipsis whether to shift lines or not.
1882 anyLineIsEliped |= ellipsis;
1884 // Whether the last line has been laid-out.
1885 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1887 if(numberOfLines == linesCapacity)
1889 // Reserve more space for the next lines.
1890 linesBuffer = ResizeLinesBuffer(lines,
1893 updateCurrentBuffer);
1896 // Updates the current text's layout with the line's layout.
1897 UpdateTextLayout(layoutParameters,
1905 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1907 if((nextIndex == totalNumberOfGlyphs) &&
1908 layoutParameters.isLastNewParagraph &&
1909 (mLayout == MULTI_LINE_BOX))
1911 // The last character of the text is a new paragraph character.
1912 // An extra line with no characters is added to increase the text's height
1913 // in order to place the cursor.
1915 if(numberOfLines == linesCapacity)
1917 // Reserve more space for the next lines.
1918 linesBuffer = ResizeLinesBuffer(lines,
1921 updateCurrentBuffer);
1924 UpdateTextLayout(layoutParameters,
1925 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1926 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1930 } // whether to add a last line.
1932 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1934 if((nullptr != bidirectionalLineInfo) &&
1935 !bidirectionalLineInfo->isIdentity &&
1936 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1938 SetGlyphPositions(layoutParameters,
1939 glyphPositionsBuffer,
1940 layoutBidiParameters,
1945 // Sets the positions of the glyphs.
1946 SetGlyphPositions(layoutParameters,
1947 glyphPositionsBuffer,
1951 // Updates the vertical pen's position.
1952 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
1954 // Increase the glyph index.
1957 } // end for() traversing glyphs.
1959 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1960 if(anyLineIsEliped && numberOfLines > 1u)
1962 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1964 Length lineIndex = 0;
1965 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1967 LineRun& delLine = linesBuffer[lineIndex];
1968 delLine.ellipsis = true;
1970 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1971 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1973 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1974 linesBuffer[lineIndex].ellipsis = false;
1978 linesBuffer[0u].ellipsis = true;
1980 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1982 Length middleLineIndex = (numberOfLines) / 2u;
1983 Length ellipsisLineIndex = 0u;
1984 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1986 LineRun& delLine = linesBuffer[middleLineIndex];
1987 delLine.ellipsis = true;
1989 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1990 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1992 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1993 linesBuffer[lineIndex].ellipsis = false;
1996 ellipsisLineIndex = middleLineIndex - 1u;
1997 middleLineIndex = (numberOfLines) / 2u;
2000 linesBuffer[ellipsisLineIndex].ellipsis = true;
2004 if(updateCurrentBuffer)
2006 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
2007 newGlyphPositions.Begin(),
2008 newGlyphPositions.End());
2009 glyphPositions.Resize(totalNumberOfGlyphs);
2011 newLines.Resize(numberOfLines);
2013 // Current text's layout size adds only the newly laid-out lines.
2014 // Updates the layout size with the previously laid-out lines.
2015 UpdateLayoutSize(lines,
2018 if(0u != newLines.Count())
2020 const LineRun& lastLine = *(newLines.End() - 1u);
2022 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
2023 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
2025 // Update the indices of the runs before the new laid-out lines are inserted.
2026 UpdateLineIndexOffsets(layoutParameters,
2031 // Insert the lines.
2032 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
2039 lines.Resize(numberOfLines);
2042 // Rounds upward to avoid a non integer size.
2043 layoutSize.height = std::ceil(layoutSize.height);
2045 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
2050 void Align(const Size& size,
2051 CharacterIndex startIndex,
2052 Length numberOfCharacters,
2053 Text::HorizontalAlignment::Type horizontalAlignment,
2054 Vector<LineRun>& lines,
2055 float& alignmentOffset,
2056 Dali::LayoutDirection::Type layoutDirection,
2057 bool matchLayoutDirection)
2059 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
2061 alignmentOffset = MAX_FLOAT;
2062 // Traverse all lines and align the glyphs.
2063 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
2067 LineRun& line = *it;
2069 if(line.characterRun.characterIndex < startIndex)
2071 // Do not align lines which have already been aligned.
2075 if(line.characterRun.characterIndex > lastCharacterPlusOne)
2077 // Do not align lines beyond the last laid-out character.
2081 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
2083 // Do not align lines beyond the last laid-out character unless the line is last and empty.
2087 // Calculate the line's alignment offset accordingly with the align option,
2088 // the box width, line length, and the paragraph's direction.
2089 CalculateHorizontalAlignment(size.width,
2090 horizontalAlignment,
2093 matchLayoutDirection);
2095 // Updates the alignment offset.
2096 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2100 void CalculateHorizontalAlignment(float boxWidth,
2101 HorizontalAlignment::Type horizontalAlignment,
2103 Dali::LayoutDirection::Type layoutDirection,
2104 bool matchLayoutDirection)
2106 line.alignmentOffset = 0.f;
2107 const bool isLineRTL = RTL == line.direction;
2109 // Whether to swap the alignment.
2110 // 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.
2111 bool isLayoutRTL = isLineRTL;
2112 float lineLength = line.width;
2114 // match align for system language direction
2115 if(matchLayoutDirection)
2117 // Swap the alignment type if the line is right to left.
2118 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2120 // Calculate the horizontal line offset.
2121 switch(horizontalAlignment)
2123 case HorizontalAlignment::BEGIN:
2129 lineLength += line.extraLength;
2132 line.alignmentOffset = boxWidth - lineLength;
2136 line.alignmentOffset = 0.f;
2140 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2141 line.alignmentOffset -= line.extraLength;
2146 case HorizontalAlignment::CENTER:
2148 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2152 line.alignmentOffset -= line.extraLength;
2155 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2158 case HorizontalAlignment::END:
2162 line.alignmentOffset = 0.f;
2166 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2167 line.alignmentOffset -= line.extraLength;
2174 lineLength += line.extraLength;
2177 line.alignmentOffset = boxWidth - lineLength;
2184 void Initialize(LineRun& line)
2186 line.glyphRun.glyphIndex = 0u;
2187 line.glyphRun.numberOfGlyphs = 0u;
2188 line.characterRun.characterIndex = 0u;
2189 line.characterRun.numberOfCharacters = 0u;
2191 line.ascender = 0.f;
2192 line.descender = 0.f;
2193 line.extraLength = 0.f;
2194 line.alignmentOffset = 0.f;
2195 line.direction = LTR;
2196 line.ellipsis = false;
2197 line.lineSpacing = mDefaultLineSpacing;
2198 line.isSplitToTwoHalves = false;
2199 line.glyphRunSecondHalf.glyphIndex = 0u;
2200 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2201 line.characterRunForSecondHalfLine.characterIndex = 0u;
2202 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2207 float mDefaultLineSpacing;
2208 float mDefaultLineSize;
2210 IntrusivePtr<Metrics> mMetrics;
2211 float mRelativeLineSize;
2217 mImpl = new Engine::Impl();
2225 void Engine::SetMetrics(MetricsPtr& metrics)
2227 mImpl->mMetrics = metrics;
2230 void Engine::SetLayout(Type layout)
2232 mImpl->mLayout = layout;
2235 Engine::Type Engine::GetLayout() const
2237 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2238 return mImpl->mLayout;
2241 void Engine::SetCursorWidth(int width)
2243 mImpl->mCursorWidth = static_cast<float>(width);
2246 int Engine::GetCursorWidth() const
2248 return static_cast<int>(mImpl->mCursorWidth);
2251 bool Engine::LayoutText(Parameters& layoutParameters,
2253 bool elideTextEnabled,
2254 bool& isAutoScrollEnabled,
2255 DevelText::EllipsisPosition::Type ellipsisPosition)
2257 return mImpl->LayoutText(layoutParameters,
2260 isAutoScrollEnabled,
2264 void Engine::Align(const Size& size,
2265 CharacterIndex startIndex,
2266 Length numberOfCharacters,
2267 Text::HorizontalAlignment::Type horizontalAlignment,
2268 Vector<LineRun>& lines,
2269 float& alignmentOffset,
2270 Dali::LayoutDirection::Type layoutDirection,
2271 bool matchLayoutDirection)
2276 horizontalAlignment,
2280 matchLayoutDirection);
2283 void Engine::SetDefaultLineSpacing(float lineSpacing)
2285 mImpl->mDefaultLineSpacing = lineSpacing;
2288 float Engine::GetDefaultLineSpacing() const
2290 return mImpl->mDefaultLineSpacing;
2293 void Engine::SetDefaultLineSize(float lineSize)
2295 mImpl->mDefaultLineSize = lineSize;
2298 float Engine::GetDefaultLineSize() const
2300 return mImpl->mDefaultLineSize;
2303 void Engine::SetRelativeLineSize(float relativeLineSize)
2305 mImpl->mRelativeLineSize = relativeLineSize;
2308 float Engine::GetRelativeLineSize() const
2310 return mImpl->mRelativeLineSize;
2313 } // namespace Layout
2317 } // namespace Toolkit