2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-engine-helper-functions.h>
32 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
40 float GetLineHeight(const LineRun lineRun, bool isLastLine)
42 // The line height is the addition of the line ascender, the line descender and the line spacing.
43 // However, the line descender has a negative value, hence the subtraction.
44 // In case this is the only/last line then line spacing should be ignored.
45 float lineHeight = lineRun.ascender - lineRun.descender;
47 if(!isLastLine || lineRun.lineSpacing > 0)
49 lineHeight += lineRun.lineSpacing;
58 #if defined(DEBUG_ENABLED)
59 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
62 const float MAX_FLOAT = std::numeric_limits<float>::max();
63 const CharacterDirection LTR = false;
64 const CharacterDirection RTL = !LTR;
65 const float LINE_SPACING = 0.f;
66 const float MIN_LINE_SIZE = 0.f;
67 const Character HYPHEN_UNICODE = 0x002D;
68 const float RELATIVE_LINE_SIZE = 1.f;
70 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
72 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
78 * @brief Stores temporary layout info of the line.
86 numberOfCharacters{0u},
93 whiteSpaceLengthEndOfLine{0.f},
95 isSplitToTwoHalves(false),
96 glyphIndexInSecondHalfLine{0u},
97 characterIndexInSecondHalfLine{0u},
98 numberOfGlyphsInSecondHalfLine{0u},
99 numberOfCharactersInSecondHalfLine{0u},
100 relativeLineSize{1.0f}
114 numberOfCharacters = 0u;
115 ascender = -MAX_FLOAT;
116 descender = MAX_FLOAT;
118 isSplitToTwoHalves = false;
119 glyphIndexInSecondHalfLine = 0u;
120 characterIndexInSecondHalfLine = 0u;
121 numberOfGlyphsInSecondHalfLine = 0u;
122 numberOfCharactersInSecondHalfLine = 0u;
123 relativeLineSize = 1.0f;
126 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
127 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
128 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
129 Length numberOfCharacters; ///< The number of characters which fit in one line.
130 float ascender; ///< The maximum ascender of all fonts in the line.
131 float descender; ///< The minimum descender of all fonts in the line.
132 float lineSpacing; ///< The line spacing
133 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
134 float previousAdvance; ///< The advance of the previous glyph.
135 float length; ///< The current length of the line.
136 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
137 CharacterDirection direction;
139 bool isSplitToTwoHalves; ///< Whether the second half is defined.
140 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
141 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
142 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
143 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
145 float relativeLineSize; ///< The relative line size to be applied for this line.
148 struct LayoutBidiParameters
152 paragraphDirection = LTR;
153 bidiParagraphIndex = 0u;
155 isBidirectional = false;
158 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
159 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
160 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
161 bool isBidirectional = false; ///< Whether the text is bidirectional.
167 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
169 mDefaultLineSpacing{LINE_SPACING},
170 mDefaultLineSize{MIN_LINE_SIZE},
171 mRelativeLineSize{RELATIVE_LINE_SIZE}
176 * @brief get the line spacing.
178 * @param[in] textSize The text size.
179 * @param[in] relativeLineSize The relative line size to be applied.
180 * @return the line spacing value.
182 float GetLineSpacing(float textSize, float relativeLineSize)
187 // Sets the line size
188 lineSpacing = mDefaultLineSize - textSize;
189 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
191 // Add the line spacing
192 lineSpacing += mDefaultLineSpacing;
194 //subtract line spcaing if relativeLineSize < 1 & larger than min height
195 relTextSize = textSize * relativeLineSize;
196 if(relTextSize > mDefaultLineSize)
198 if(relativeLineSize < 1)
200 //subtract the difference (always will be positive)
201 lineSpacing -= (textSize - relTextSize);
205 //reverse the addition in the top.
206 if(mDefaultLineSize > textSize)
208 lineSpacing -= mDefaultLineSize - textSize;
211 //add difference instead
212 lineSpacing += relTextSize - textSize;
220 * @brief Updates the line ascender and descender with the metrics of a new font.
222 * @param[in] glyphMetrics The metrics of the new font.
223 * @param[in,out] lineLayout The line layout.
225 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
227 Text::FontMetrics fontMetrics;
228 if(0u != glyphMetrics.fontId)
230 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
234 fontMetrics.ascender = glyphMetrics.fontHeight;
235 fontMetrics.descender = 0.f;
236 fontMetrics.height = fontMetrics.ascender;
237 fontMetrics.underlinePosition = 0.f;
238 fontMetrics.underlineThickness = 1.f;
241 // Sets the maximum ascender.
242 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
244 // Sets the minimum descender.
245 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
247 lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender, lineLayout.relativeLineSize);
251 * @brief Merges a temporary line layout into the line layout.
253 * @param[in,out] lineLayout The line layout.
254 * @param[in] tmpLineLayout A temporary line layout.
255 * @param[in] isShifted Whether to shift first glyph and character indices.
257 void MergeLineLayout(LineLayout& lineLayout,
258 const LineLayout& tmpLineLayout,
261 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
262 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
264 lineLayout.penX = tmpLineLayout.penX;
265 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
267 lineLayout.length = tmpLineLayout.length;
268 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
270 // Sets the maximum ascender.
271 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
273 // Sets the minimum descender.
274 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
276 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
279 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
280 lineLayout.characterIndex = tmpLineLayout.characterIndex;
283 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
284 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
285 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
286 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
287 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
290 void LayoutRightToLeft(const Parameters& parameters,
291 const BidirectionalLineInfoRun& bidirectionalLineInfo,
293 float& whiteSpaceLengthEndOfLine)
295 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
296 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
297 // then the first half of line "characterRun",
298 // Otherwise travers whole characters in"characterRun".
300 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
301 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
302 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
303 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
305 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
306 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
307 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
309 CharacterIndex characterLogicalIndex = 0u;
310 CharacterIndex characterVisualIndex = 0u;
312 float calculatedAdvance = 0.f;
314 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
315 // Otherwise maps the first visual index from visualToLogicalMap.
316 // This is to initialize the first visual index.
317 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
319 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
323 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
326 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
328 if(RTL == bidirectionalLineInfo.direction)
330 // If there are characters in the second half of Line.
331 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
333 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
334 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
336 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
338 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
339 whiteSpaceLengthEndOfLine += calculatedAdvance;
341 ++characterLogicalIndex;
342 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
346 // If all characters in the second half of Line are WhiteSpaces.
347 // then continue adding the WhiteSpaces from the first hel of Line.
348 // Also this is valid when the line was not splitted.
349 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
351 extendedToSecondHalf = true; // Whether the logical index is extended to second half
352 characterLogicalIndex = 0u;
353 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
355 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
356 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
358 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
360 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
361 whiteSpaceLengthEndOfLine += calculatedAdvance;
363 ++characterLogicalIndex;
364 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
369 // Here's the first index of character is not WhiteSpace
370 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
372 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
373 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
374 lastGlyphOfParagraphPlusOne,
375 charactersPerGlyphBuffer);
377 GlyphMetrics glyphMetrics;
378 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
379 GetGlyphsMetrics(glyphIndex,
380 numberOfGLyphsInGroup,
386 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
388 // Traverses the characters of the right to left paragraph.
389 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
390 if(!extendedToSecondHalf &&
391 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
393 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
395 // Convert the character in the logical order into the character in the visual order.
396 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
397 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
399 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
401 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
402 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
403 lastGlyphOfParagraphPlusOne,
404 charactersPerGlyphBuffer);
406 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
408 GlyphMetrics glyphMetrics;
409 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
410 GetGlyphsMetrics(glyphIndex,
411 numberOfGLyphsInGroup,
419 // If glyph is WhiteSpace then:
420 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
421 // the endOfLine in RTL was the headOfLine for layouting.
422 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
423 if(RTL == bidirectionalLineInfo.direction)
425 length += glyphMetrics.advance;
429 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
431 penX += glyphMetrics.advance;
435 // If glyph is not whiteSpace then:
436 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
437 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
438 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
439 // Otherwise the current length is maximum.
440 if(LTR == bidirectionalLineInfo.direction)
442 whiteSpaceLengthEndOfLine = 0.f;
444 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
445 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
450 // Continue traversing in the first half of line or in the whole line.
451 // If the second half of line was extended then continue from logical index in the first half of line
452 // Also this is valid when the line was not splitted and there were WhiteSpace.
453 // Otherwise start from first logical index in line.
454 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
455 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
457 // Convert the character in the logical order into the character in the visual order.
458 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
459 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
461 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
463 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
464 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
465 lastGlyphOfParagraphPlusOne,
466 charactersPerGlyphBuffer);
468 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
470 GlyphMetrics glyphMetrics;
471 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
472 GetGlyphsMetrics(glyphIndex,
473 numberOfGLyphsInGroup,
481 // If glyph is WhiteSpace then:
482 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
483 // the endOfLine in RTL was the headOfLine for layouting.
484 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
485 if(RTL == bidirectionalLineInfo.direction)
487 length += glyphMetrics.advance;
491 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
493 penX += glyphMetrics.advance;
497 // If glyph is not whiteSpace then:
498 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
499 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
500 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
501 // Otherwise the current length is maximum.
502 if(LTR == bidirectionalLineInfo.direction)
504 whiteSpaceLengthEndOfLine = 0.f;
506 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
507 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
512 void ReorderBiDiLayout(const Parameters& parameters,
513 LayoutBidiParameters& bidiParameters,
514 const LineLayout& currentLineLayout,
515 LineLayout& lineLayout,
516 bool breakInCharacters,
517 bool enforceEllipsisInSingleLine)
519 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
521 // The last glyph to be laid-out.
522 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
524 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
526 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
527 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
528 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
530 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
532 // Sets the visual to logical map tables needed to reorder the text.
533 ReorderLine(bidirectionalParagraphInfo,
534 bidirectionalLinesInfo,
535 bidiParameters.bidiLineIndex,
536 lineLayout.characterIndex,
537 lineLayout.numberOfCharacters,
538 lineLayout.characterIndexInSecondHalfLine,
539 lineLayout.numberOfCharactersInSecondHalfLine,
540 bidiParameters.paragraphDirection);
542 // Recalculate the length of the line and update the layout.
543 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
545 if(!bidirectionalLineInfo.isIdentity)
548 float whiteSpaceLengthEndOfLine = 0.f;
549 LayoutRightToLeft(parameters,
550 bidirectionalLineInfo,
552 whiteSpaceLengthEndOfLine);
554 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
555 if(!Equals(length, lineLayout.length))
557 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
559 if(isMultiline && (length > parameters.boundingBox.width))
561 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
563 // The word doesn't fit in one line. It has to be split by character.
565 // Remove the last laid out glyph(s) as they doesn't fit.
566 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
568 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
569 lastGlyphOfParagraphPlusOne,
570 charactersPerGlyphBuffer);
572 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
574 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
575 lineLayout.numberOfCharacters -= numberOfCharacters;
577 AdjustLayout(parameters,
579 bidirectionalParagraphInfo,
582 if(lineLayout.length < parameters.boundingBox.width)
587 if(glyphIndex < numberOfGLyphsInGroup)
589 // avoids go under zero for an unsigned int.
593 glyphIndex -= numberOfGLyphsInGroup;
598 lineLayout = currentLineLayout;
600 AdjustLayout(parameters,
602 bidirectionalParagraphInfo,
608 lineLayout.length = std::max(length, lineLayout.length);
615 void AdjustLayout(const Parameters& parameters,
616 LayoutBidiParameters& bidiParameters,
617 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
618 LineLayout& lineLayout)
620 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
622 // Remove current reordered line.
623 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
625 // Re-build the conversion table without the removed glyphs.
626 ReorderLine(bidirectionalParagraphInfo,
627 bidirectionalLinesInfo,
628 bidiParameters.bidiLineIndex,
629 lineLayout.characterIndex,
630 lineLayout.numberOfCharacters,
631 lineLayout.characterIndexInSecondHalfLine,
632 lineLayout.numberOfCharactersInSecondHalfLine,
633 bidiParameters.paragraphDirection);
635 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
638 float whiteSpaceLengthEndOfLine = 0.f;
639 LayoutRightToLeft(parameters,
640 bidirectionalLineInfo,
642 whiteSpaceLengthEndOfLine);
644 lineLayout.length = length;
645 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
649 * Retrieves the line layout for a given box width.
651 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
652 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
653 * to reorder the line and recalculate its length.
656 * @param[in] parameters The layout parameters.
657 * @param[] bidiParameters Bidirectional info for the current line.
658 * @param[out] lineLayout The line layout.
659 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
660 * @param[in] ellipsisPosition Where is the location the text elide
662 void GetLineLayoutForBox(const Parameters& parameters,
663 LayoutBidiParameters& bidiParameters,
664 LineLayout& lineLayout,
666 DevelText::EllipsisPosition::Type ellipsisPosition,
667 bool enforceEllipsisInSingleLine,
668 bool elideTextEnabled)
670 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
671 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
673 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
674 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
675 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
676 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
677 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
679 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
680 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
682 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
683 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
684 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
685 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
686 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
687 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
689 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
691 // The last glyph to be laid-out.
692 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
694 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
695 // In the case the line starts with a right to left character, if the width is longer than the advance,
696 // the difference needs to be added to the line length.
698 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
699 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
700 lastGlyphOfParagraphPlusOne,
701 charactersPerGlyphBuffer);
703 float targetWidth = parameters.boundingBox.width;
704 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
706 bool isSecondHalf = false;
708 const float characterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
709 float calculatedAdvance = 0.f;
710 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
711 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
713 GlyphMetrics glyphMetrics;
714 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
715 GetGlyphsMetrics(lineLayout.glyphIndex,
716 numberOfGLyphsInGroup,
722 // Set the direction of the first character of the line.
723 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
725 // Stores temporary line layout which has not been added to the final line layout.
726 LineLayout tmpLineLayout;
728 // Initialize the start point.
730 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
731 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
732 // 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.
733 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
735 tmpLineLayout.relativeLineSize = lineLayout.relativeLineSize;
737 // Calculate the line height if there is no characters.
738 FontId lastFontId = glyphMetrics.fontId;
739 UpdateLineHeight(glyphMetrics, tmpLineLayout);
741 bool oneWordLaidOut = false;
742 bool oneHyphenLaidOut = false;
743 GlyphIndex hyphenIndex = 0;
744 GlyphInfo hyphenGlyph;
746 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
747 glyphIndex < lastGlyphOfParagraphPlusOne;)
749 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
751 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
752 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
753 lastGlyphOfParagraphPlusOne,
754 charactersPerGlyphBuffer);
756 GlyphMetrics glyphMetrics;
757 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
758 GetGlyphsMetrics(glyphIndex,
759 numberOfGLyphsInGroup,
765 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
767 // Check if the font of the current glyph is the same of the previous one.
768 // If it's different the ascender and descender need to be updated.
769 if(lastFontId != glyphMetrics.fontId)
771 UpdateLineHeight(glyphMetrics, tmpLineLayout);
772 lastFontId = glyphMetrics.fontId;
775 // Get the character indices for the current glyph. The last character index is needed
776 // because there are glyphs formed by more than one character but their break info is
777 // given only for the last character.
778 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
779 const bool hasCharacters = charactersPerGlyph > 0u;
780 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
781 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
783 // Get the line break info for the current character.
784 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
788 // Increase the number of characters.
789 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
791 // Increase the number of glyphs.
792 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
796 // Increase the number of characters.
797 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
799 // Increase the number of glyphs.
800 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
803 // Check whether is a white space.
804 const Character character = *(textBuffer + characterFirstIndex);
805 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
807 // Calculate the length of the line.
809 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
810 const float previousTmpPenX = tmpLineLayout.penX;
811 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
812 const float previousTmpLength = tmpLineLayout.length;
813 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
817 // Add the length to the length of white spaces at the end of the line.
818 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
819 // The advance is used as the width is always zero for the white spaces.
823 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
824 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
826 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
828 // Clear the white space length at the end of the line.
829 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
832 if(isSplitToTwoHalves && (!isSecondHalf) &&
833 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
835 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
836 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
838 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
839 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
841 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
842 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
844 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
846 // Check if the accumulated length fits in the width of the box.
847 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
848 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
849 completelyFill && !isMultiline &&
850 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
852 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
854 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
856 GlyphMetrics glyphMetrics;
857 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
858 GetGlyphsMetrics(glyphIndexToRemove,
859 numberOfGLyphsInGroup,
865 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
866 lastGlyphOfParagraphPlusOne,
867 charactersPerGlyphBuffer);
869 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
870 const bool hasCharacters = charactersPerGlyph > 0u;
871 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
872 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
874 // Check whether is a white space.
875 const Character character = *(textBuffer + characterFirstIndex);
876 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
880 // Decrease the number of characters for SecondHalf.
881 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
883 // Decrease the number of glyphs for SecondHalf.
884 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
888 // Decrease the number of characters.
889 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
891 // Decrease the number of glyphs.
892 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
895 if(isRemovedGlyphWhiteSpace)
897 tmpLineLayout.penX -= glyphMetrics.advance;
898 tmpLineLayout.length -= glyphMetrics.advance;
902 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
903 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
908 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
909 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
910 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
914 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
915 tmpLineLayout.characterIndex = characterLastIndex + 1u;
916 glyphIndexToRemove = tmpLineLayout.glyphIndex;
920 else if((completelyFill || isMultiline) &&
921 (tmpLineLayout.length > targetWidth))
923 // Current word does not fit in the box's width.
924 if(((oneHyphenLaidOut && isHyphenMode) ||
925 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
928 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
929 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
932 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
934 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
936 // The word doesn't fit in the control's width. It needs to be split by character.
937 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
941 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
942 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
946 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
947 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
950 tmpLineLayout.penX = previousTmpPenX;
951 tmpLineLayout.previousAdvance = previousTmpAdvance;
952 tmpLineLayout.length = previousTmpLength;
953 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
956 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
958 // Add part of the word to the line layout and shift the first glyph.
959 MergeLineLayout(lineLayout, tmpLineLayout, true);
961 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
962 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
964 // Add part of the word to the line layout.
965 MergeLineLayout(lineLayout, tmpLineLayout, false);
970 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
973 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
975 // Reorder the RTL line.
976 if(bidiParameters.isBidirectional)
978 ReorderBiDiLayout(parameters,
983 enforceEllipsisInSingleLine);
989 if((isMultiline || isLastGlyph) &&
990 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
992 LineLayout currentLineLayout = lineLayout;
993 oneHyphenLaidOut = false;
995 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
997 // Must break the line. Update the line layout, shift the first glyph and return.
998 MergeLineLayout(lineLayout, tmpLineLayout, true);
1002 // Must break the line. Update the line layout and return.
1003 MergeLineLayout(lineLayout, tmpLineLayout, false);
1006 // Reorder the RTL line.
1007 if(bidiParameters.isBidirectional)
1009 ReorderBiDiLayout(parameters,
1014 enforceEllipsisInSingleLine);
1017 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
1018 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1024 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1026 oneHyphenLaidOut = false;
1027 oneWordLaidOut = isWordLaidOut;
1028 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
1030 // Current glyph is the last one of the current word.
1031 // Add the temporal layout to the current one.
1032 MergeLineLayout(lineLayout, tmpLineLayout, false);
1034 tmpLineLayout.Clear();
1038 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1039 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1041 hyphenGlyph = GlyphInfo();
1042 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1044 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1045 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1047 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1049 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1051 hyphenIndex = glyphIndex;
1052 oneHyphenLaidOut = true;
1054 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1056 // Current glyph is the last one of the current word hyphen.
1057 // Add the temporal layout to the current one.
1058 MergeLineLayout(lineLayout, tmpLineLayout, false);
1060 tmpLineLayout.Clear();
1064 glyphIndex += numberOfGLyphsInGroup;
1067 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1070 void SetGlyphPositions(const Parameters& layoutParameters,
1071 Vector2* glyphPositionsBuffer,
1072 const LineLayout& layout)
1074 // Traverse the glyphs and set the positions.
1076 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1077 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1078 const Length numberOfGlyphs = layout.numberOfGlyphs;
1079 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1081 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1082 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1084 // Check if the x bearing of the first character is negative.
1085 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1086 // so the penX position needs to be moved to the right.
1087 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1088 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1090 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1091 layoutParameters.textModel->mLogicalModel,
1092 interGlyphExtraAdvance,
1094 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1095 startIndexForGlyphPositions,
1096 glyphPositionsBuffer,
1099 if(layout.isSplitToTwoHalves)
1101 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1102 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1103 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1105 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1106 layoutParameters.textModel->mLogicalModel,
1107 interGlyphExtraAdvance,
1108 numberOfGlyphsInSecondHalfLine,
1109 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1110 startIndexForGlyphPositionsnSecondHalf,
1111 glyphPositionsBuffer,
1116 void SetGlyphPositions(const Parameters& layoutParameters,
1117 Vector2* glyphPositionsBuffer,
1118 LayoutBidiParameters& layoutBidiParameters,
1119 const LineLayout& layout)
1121 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1122 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1123 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1125 CharacterIndex characterLogicalIndex = 0u;
1126 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1127 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1131 if(layout.isSplitToTwoHalves)
1133 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1134 layoutParameters.textModel->mLogicalModel,
1135 layoutBidiParameters.bidiLineIndex,
1136 layoutParameters.startGlyphIndex,
1137 glyphPositionsBuffer,
1138 characterVisualIndex,
1139 characterLogicalIndex,
1143 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1145 extendedToSecondHalf = true;
1146 characterLogicalIndex = 0u;
1147 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1149 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1150 layoutParameters.textModel->mLogicalModel,
1151 layoutBidiParameters.bidiLineIndex,
1152 layoutParameters.startGlyphIndex,
1153 glyphPositionsBuffer,
1154 characterVisualIndex,
1155 characterLogicalIndex,
1159 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1160 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1162 penX += -glyph.xBearing;
1164 // Traverses the characters of the right to left paragraph.
1165 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1167 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1168 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1169 layoutParameters.startGlyphIndex,
1170 layoutParameters.interGlyphExtraAdvance,
1171 bidiLine.characterRunForSecondHalfLine,
1172 bidiLine.visualToLogicalMapSecondHalf,
1173 glyphPositionsBuffer,
1174 characterLogicalIndex,
1178 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1180 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1181 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1182 layoutParameters.startGlyphIndex,
1183 layoutParameters.interGlyphExtraAdvance,
1184 bidiLine.characterRun,
1185 bidiLine.visualToLogicalMap,
1186 glyphPositionsBuffer,
1187 characterLogicalIndex,
1192 * @brief Resizes the line buffer.
1194 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1195 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1196 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1197 * @param[in] updateCurrentBuffer Whether the layout is updated.
1199 * @return Pointer to either lines or newLines.
1201 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1202 Vector<LineRun>& newLines,
1203 Length& linesCapacity,
1204 bool updateCurrentBuffer)
1206 LineRun* linesBuffer = nullptr;
1207 // Reserve more space for the next lines.
1208 linesCapacity *= 2u;
1209 if(updateCurrentBuffer)
1211 newLines.Resize(linesCapacity);
1212 linesBuffer = newLines.Begin();
1216 lines.Resize(linesCapacity);
1217 linesBuffer = lines.Begin();
1224 * Ellipsis a line if it exceeds the width's of the bounding box.
1226 * @param[in] layoutParameters The parameters needed to layout the text.
1227 * @param[in] layout The line layout.
1228 * @param[in,out] layoutSize The text's layout size.
1229 * @param[in,out] linesBuffer Pointer to the line's buffer.
1230 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1231 * @param[in,out] numberOfLines The number of laid-out lines.
1232 * @param[in] penY The vertical layout position.
1233 * @param[in] currentParagraphDirection The current paragraph's direction.
1234 * @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
1235 * @param[in] ellipsisPosition Where is the location the text elide
1237 * return Whether the line is ellipsized.
1239 bool EllipsisLine(const Parameters& layoutParameters,
1240 LayoutBidiParameters& layoutBidiParameters,
1241 const LineLayout& layout,
1243 LineRun* linesBuffer,
1244 Vector2* glyphPositionsBuffer,
1245 Length& numberOfLines,
1247 bool& isAutoScrollEnabled,
1248 DevelText::EllipsisPosition::Type ellipsisPosition,
1249 bool enforceEllipsisInSingleLine)
1251 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))));
1252 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1253 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1255 isAutoScrollEnabled = false;
1256 // Do not layout more lines if ellipsis is enabled.
1258 // The last line needs to be completely filled with characters.
1259 // Part of a word may be used.
1261 LineRun* lineRun = nullptr;
1262 LineLayout ellipsisLayout;
1264 ellipsisLayout.relativeLineSize = layout.relativeLineSize;
1266 if(0u != numberOfLines)
1268 // Get the last line and layout it again with the 'completelyFill' flag to true.
1269 lineRun = linesBuffer + (numberOfLines - 1u);
1270 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1272 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1276 // At least there is space reserved for one line.
1277 lineRun = linesBuffer;
1279 lineRun->glyphRun.glyphIndex = 0u;
1280 ellipsisLayout.glyphIndex = 0u;
1281 lineRun->isSplitToTwoHalves = false;
1286 GetLineLayoutForBox(layoutParameters,
1287 layoutBidiParameters,
1291 enforceEllipsisInSingleLine,
1294 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1296 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1299 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1300 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1301 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1302 lineRun->width = ellipsisLayout.length;
1303 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1304 lineRun->ascender = ellipsisLayout.ascender;
1305 lineRun->descender = ellipsisLayout.descender;
1306 lineRun->ellipsis = true;
1308 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1309 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1310 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1311 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1312 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1314 layoutSize.width = layoutParameters.boundingBox.width;
1315 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1317 layoutSize.height += GetLineHeight(*lineRun, true);
1321 //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1322 //usually we don't includ it in normal cases using GetLineHeight()
1323 if(lineRun->lineSpacing < 0)
1325 layoutSize.height -= lineRun->lineSpacing;
1329 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1331 if(layoutBidiParameters.isBidirectional)
1333 layoutBidiParameters.bidiLineIndex = 0u;
1334 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1335 endIt = bidirectionalLinesInfo.End();
1337 ++it, ++layoutBidiParameters.bidiLineIndex)
1339 const BidirectionalLineInfoRun& run = *it;
1340 //To handle case when the laid characters exist in next line.
1341 //More than one BidirectionalLineInfoRun could start with same character.
1342 //When need to check also numberOfCharacters in line.
1343 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1344 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1345 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1346 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1347 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1349 // Found where to insert the bidi line info.
1355 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1357 if((nullptr != bidirectionalLineInfo) &&
1358 !bidirectionalLineInfo->isIdentity &&
1359 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1361 lineRun->direction = RTL;
1362 SetGlyphPositions(layoutParameters,
1363 glyphPositionsBuffer,
1364 layoutBidiParameters,
1369 lineRun->direction = LTR;
1370 SetGlyphPositions(layoutParameters,
1371 glyphPositionsBuffer,
1380 * @brief Updates the text layout with a new laid-out line.
1382 * @param[in] layoutParameters The parameters needed to layout the text.
1383 * @param[in] layout The line layout.
1384 * @param[in,out] layoutSize The text's layout size.
1385 * @param[in,out] linesBuffer Pointer to the line's buffer.
1386 * @param[in] index Index to the vector of glyphs.
1387 * @param[in,out] numberOfLines The number of laid-out lines.
1388 * @param[in] isLastLine Whether the laid-out line is the last one.
1390 void UpdateTextLayout(const Parameters& layoutParameters,
1391 const LineLayout& layout,
1393 LineRun* linesBuffer,
1395 Length& numberOfLines,
1398 LineRun& lineRun = *(linesBuffer + numberOfLines);
1401 lineRun.glyphRun.glyphIndex = index;
1402 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1403 lineRun.characterRun.characterIndex = layout.characterIndex;
1404 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1405 lineRun.width = layout.length;
1406 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1408 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1409 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1410 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1411 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1412 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1414 // Rounds upward to avoid a non integer size.
1415 lineRun.width = std::ceil(lineRun.width);
1417 lineRun.ascender = layout.ascender;
1418 lineRun.descender = layout.descender;
1419 lineRun.direction = layout.direction;
1420 lineRun.ellipsis = false;
1422 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
1424 // Update the actual size.
1425 if(lineRun.width > layoutSize.width)
1427 layoutSize.width = lineRun.width;
1430 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1434 * @brief Updates the text layout with the last laid-out line.
1436 * @param[in] layoutParameters The parameters needed to layout the text.
1437 * @param[in] characterIndex The character index of the line.
1438 * @param[in] glyphIndex The glyph index of the line.
1439 * @param[in,out] layoutSize The text's layout size.
1440 * @param[in,out] linesBuffer Pointer to the line's buffer.
1441 * @param[in,out] numberOfLines The number of laid-out lines.
1443 void UpdateTextLayout(const Parameters& layoutParameters,
1444 CharacterIndex characterIndex,
1445 GlyphIndex glyphIndex,
1447 LineRun* linesBuffer,
1448 Length& numberOfLines)
1450 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1452 // Need to add a new line with no characters but with height to increase the layoutSize.height
1453 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1455 Text::FontMetrics fontMetrics;
1456 if(0u != glyphInfo.fontId)
1458 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1461 LineRun& lineRun = *(linesBuffer + numberOfLines);
1464 lineRun.glyphRun.glyphIndex = glyphIndex;
1465 lineRun.glyphRun.numberOfGlyphs = 0u;
1466 lineRun.characterRun.characterIndex = characterIndex;
1467 lineRun.characterRun.numberOfCharacters = 0u;
1468 lineRun.width = 0.f;
1469 lineRun.ascender = fontMetrics.ascender;
1470 lineRun.descender = fontMetrics.descender;
1471 lineRun.extraLength = 0.f;
1472 lineRun.alignmentOffset = 0.f;
1473 lineRun.direction = LTR;
1474 lineRun.ellipsis = false;
1476 BoundedParagraphRun currentParagraphRun;
1477 LineLayout tempLineLayout;
1478 (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
1480 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
1482 layoutSize.height += GetLineHeight(lineRun, true);
1486 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1488 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1489 * @param[in,out] layoutSize The text's layout size.
1491 void UpdateLayoutSize(const Vector<LineRun>& lines,
1494 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1495 endIt = lines.End();
1499 const LineRun& line = *it;
1500 bool isLastLine = (it + 1 == endIt);
1502 if(line.width > layoutSize.width)
1504 layoutSize.width = line.width;
1507 layoutSize.height += GetLineHeight(line, isLastLine);
1512 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1514 * @param[in] layoutParameters The parameters needed to layout the text.
1515 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1516 * @param[in] characterOffset The offset to be added to the runs of characters.
1517 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1519 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1520 Vector<LineRun>& lines,
1521 Length characterOffset,
1524 // Update the glyph and character runs.
1525 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1526 endIt = lines.End();
1530 LineRun& line = *it;
1532 line.glyphRun.glyphIndex = glyphOffset;
1533 line.characterRun.characterIndex = characterOffset;
1535 glyphOffset += line.glyphRun.numberOfGlyphs;
1536 characterOffset += line.characterRun.numberOfCharacters;
1541 * @brief Sets the relative line size for the LineLayout
1543 * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
1544 * @param[in,out] lineLayout The line layout to be updated.
1546 void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
1548 lineLayout.relativeLineSize = mRelativeLineSize;
1550 if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
1552 lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
1557 * @brief Get the bounded paragraph for the characterIndex if exists.
1559 * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
1560 * @param[in] characterIndex The character index to get bounded paragraph for.
1561 * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
1563 * @return returns true if a bounded paragraph was found.
1565 bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
1567 for(Vector<BoundedParagraphRun>::Iterator it = boundedParagraphRuns.Begin(),
1568 endIt = boundedParagraphRuns.End();
1572 BoundedParagraphRun& tempParagraphRun = *it;
1574 if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
1575 characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
1577 currentParagraphRun = tempParagraphRun;
1585 bool LayoutText(Parameters& layoutParameters,
1587 bool elideTextEnabled,
1588 bool& isAutoScrollEnabled,
1589 DevelText::EllipsisPosition::Type ellipsisPosition)
1591 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1592 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1594 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1595 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1597 //Reset indices of ElidedGlyphs
1598 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1599 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1600 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1601 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1603 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1604 const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
1606 if(0u == layoutParameters.numberOfGlyphs)
1608 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1609 if(layoutParameters.isLastNewParagraph)
1611 Length numberOfLines = lines.Count();
1612 if(0u != numberOfLines)
1614 const LineRun& lastLine = *(lines.End() - 1u);
1616 if(0u != lastLine.characterRun.numberOfCharacters)
1618 // Need to add a new line with no characters but with height to increase the layoutSize.height
1620 Initialize(newLine);
1621 lines.PushBack(newLine);
1623 UpdateTextLayout(layoutParameters,
1624 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1625 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1633 // Calculates the layout size.
1634 UpdateLayoutSize(lines,
1637 // Rounds upward to avoid a non integer size.
1638 layoutSize.height = std::ceil(layoutSize.height);
1640 // Nothing else do if there are no glyphs to layout.
1644 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1645 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1646 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1648 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1649 // This extra line needs to be removed.
1650 if(0u != lines.Count())
1652 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1654 if((0u == lastLine->characterRun.numberOfCharacters) &&
1655 (lastGlyphPlusOne == totalNumberOfGlyphs))
1657 lines.Remove(lastLine);
1661 // Retrieve BiDi info.
1662 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1664 const CharacterIndex* const glyphsToCharactersBuffer = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
1665 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1666 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1668 // Set the layout bidirectional paramters.
1669 LayoutBidiParameters layoutBidiParameters;
1671 // Whether the layout is being updated or set from scratch.
1672 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1674 Vector2* glyphPositionsBuffer = nullptr;
1675 Vector<Vector2> newGlyphPositions;
1677 LineRun* linesBuffer = nullptr;
1678 Vector<LineRun> newLines;
1680 // Estimate the number of lines.
1681 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1682 Length numberOfLines = 0u;
1684 if(updateCurrentBuffer)
1686 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1687 glyphPositionsBuffer = newGlyphPositions.Begin();
1689 newLines.Resize(linesCapacity);
1690 linesBuffer = newLines.Begin();
1694 glyphPositionsBuffer = glyphPositions.Begin();
1696 lines.Resize(linesCapacity);
1697 linesBuffer = lines.Begin();
1700 float penY = CalculateLineOffset(lines,
1701 layoutParameters.startLineIndex);
1702 bool anyLineIsEliped = false;
1703 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1705 layoutBidiParameters.Clear();
1707 if(hasBidiParagraphs)
1709 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1711 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1712 endIt = bidirectionalParagraphsInfo.End();
1714 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1716 const BidirectionalParagraphInfoRun& run = *it;
1718 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1720 if(lastCharacterIndex <= startCharacterIndex)
1722 // Do not process, the paragraph has already been processed.
1726 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1728 layoutBidiParameters.paragraphDirection = run.direction;
1729 layoutBidiParameters.isBidirectional = true;
1732 // Has already been found.
1736 if(layoutBidiParameters.isBidirectional)
1738 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1739 endIt = bidirectionalLinesInfo.End();
1741 ++it, ++layoutBidiParameters.bidiLineIndex)
1743 const BidirectionalLineInfoRun& run = *it;
1745 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1747 if(lastCharacterIndex <= startCharacterIndex)
1753 if(startCharacterIndex < lastCharacterIndex)
1755 // Found where to insert the bidi line info.
1762 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1764 // Get the layout for the line.
1766 layout.direction = layoutBidiParameters.paragraphDirection;
1767 layout.glyphIndex = index;
1769 BoundedParagraphRun currentParagraphRun;
1770 (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
1772 GetLineLayoutForBox(layoutParameters,
1773 layoutBidiParameters,
1780 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1781 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1782 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1783 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1784 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1786 CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
1788 //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)
1789 if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex+layout.numberOfCharacters)
1791 layout.relativeLineSize = mRelativeLineSize;
1794 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1796 // The width is too small and no characters are laid-out.
1797 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1799 lines.Resize(numberOfLines);
1801 // Rounds upward to avoid a non integer size.
1802 layoutSize.height = std::ceil(layoutSize.height);
1807 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1809 penY += layout.ascender;
1811 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1813 bool ellipsis = false;
1814 if(elideTextEnabled)
1816 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1818 // Does the ellipsis of the last line.
1819 ellipsis = EllipsisLine(layoutParameters,
1820 layoutBidiParameters,
1824 glyphPositionsBuffer,
1827 isAutoScrollEnabled,
1832 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1834 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1835 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1837 ellipsis = EllipsisLine(layoutParameters,
1838 layoutBidiParameters,
1842 glyphPositionsBuffer,
1845 isAutoScrollEnabled,
1850 //clear hyphen from ellipsis line
1851 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1852 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1854 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1856 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1857 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1861 // No more lines to layout.
1866 //In START location of ellipsis whether to shift lines or not.
1867 anyLineIsEliped |= ellipsis;
1869 // Whether the last line has been laid-out.
1870 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1872 if(numberOfLines == linesCapacity)
1874 // Reserve more space for the next lines.
1875 linesBuffer = ResizeLinesBuffer(lines,
1878 updateCurrentBuffer);
1881 // Updates the current text's layout with the line's layout.
1882 UpdateTextLayout(layoutParameters,
1890 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1892 if((nextIndex == totalNumberOfGlyphs) &&
1893 layoutParameters.isLastNewParagraph &&
1894 (mLayout == MULTI_LINE_BOX))
1896 // The last character of the text is a new paragraph character.
1897 // An extra line with no characters is added to increase the text's height
1898 // in order to place the cursor.
1900 if(numberOfLines == linesCapacity)
1902 // Reserve more space for the next lines.
1903 linesBuffer = ResizeLinesBuffer(lines,
1906 updateCurrentBuffer);
1909 UpdateTextLayout(layoutParameters,
1910 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1911 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1915 } // whether to add a last line.
1917 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1919 if((nullptr != bidirectionalLineInfo) &&
1920 !bidirectionalLineInfo->isIdentity &&
1921 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1923 SetGlyphPositions(layoutParameters,
1924 glyphPositionsBuffer,
1925 layoutBidiParameters,
1930 // Sets the positions of the glyphs.
1931 SetGlyphPositions(layoutParameters,
1932 glyphPositionsBuffer,
1936 // Updates the vertical pen's position.
1937 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
1939 // Increase the glyph index.
1942 } // end for() traversing glyphs.
1944 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1945 if(anyLineIsEliped && numberOfLines > 1u)
1947 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1949 Length lineIndex = 0;
1950 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1952 LineRun& delLine = linesBuffer[lineIndex];
1953 delLine.ellipsis = true;
1955 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1956 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1958 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1959 linesBuffer[lineIndex].ellipsis = false;
1963 linesBuffer[0u].ellipsis = true;
1965 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1967 Length middleLineIndex = (numberOfLines) / 2u;
1968 Length ellipsisLineIndex = 0u;
1969 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1971 LineRun& delLine = linesBuffer[middleLineIndex];
1972 delLine.ellipsis = true;
1974 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1975 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1977 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1978 linesBuffer[lineIndex].ellipsis = false;
1981 ellipsisLineIndex = middleLineIndex - 1u;
1982 middleLineIndex = (numberOfLines) / 2u;
1985 linesBuffer[ellipsisLineIndex].ellipsis = true;
1989 if(updateCurrentBuffer)
1991 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1992 newGlyphPositions.Begin(),
1993 newGlyphPositions.End());
1994 glyphPositions.Resize(totalNumberOfGlyphs);
1996 newLines.Resize(numberOfLines);
1998 // Current text's layout size adds only the newly laid-out lines.
1999 // Updates the layout size with the previously laid-out lines.
2000 UpdateLayoutSize(lines,
2003 if(0u != newLines.Count())
2005 const LineRun& lastLine = *(newLines.End() - 1u);
2007 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
2008 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
2010 // Update the indices of the runs before the new laid-out lines are inserted.
2011 UpdateLineIndexOffsets(layoutParameters,
2016 // Insert the lines.
2017 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
2024 lines.Resize(numberOfLines);
2027 // Rounds upward to avoid a non integer size.
2028 layoutSize.height = std::ceil(layoutSize.height);
2030 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
2035 void Align(const Size& size,
2036 CharacterIndex startIndex,
2037 Length numberOfCharacters,
2038 Text::HorizontalAlignment::Type horizontalAlignment,
2039 Vector<LineRun>& lines,
2040 float& alignmentOffset,
2041 Dali::LayoutDirection::Type layoutDirection,
2042 bool matchLayoutDirection)
2044 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
2046 alignmentOffset = MAX_FLOAT;
2047 // Traverse all lines and align the glyphs.
2048 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
2052 LineRun& line = *it;
2054 if(line.characterRun.characterIndex < startIndex)
2056 // Do not align lines which have already been aligned.
2060 if(line.characterRun.characterIndex > lastCharacterPlusOne)
2062 // Do not align lines beyond the last laid-out character.
2066 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
2068 // Do not align lines beyond the last laid-out character unless the line is last and empty.
2072 // Calculate the line's alignment offset accordingly with the align option,
2073 // the box width, line length, and the paragraph's direction.
2074 CalculateHorizontalAlignment(size.width,
2075 horizontalAlignment,
2078 matchLayoutDirection);
2080 // Updates the alignment offset.
2081 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2085 void CalculateHorizontalAlignment(float boxWidth,
2086 HorizontalAlignment::Type horizontalAlignment,
2088 Dali::LayoutDirection::Type layoutDirection,
2089 bool matchLayoutDirection)
2091 line.alignmentOffset = 0.f;
2092 const bool isLineRTL = RTL == line.direction;
2094 // Whether to swap the alignment.
2095 // 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.
2096 bool isLayoutRTL = isLineRTL;
2097 float lineLength = line.width;
2099 // match align for system language direction
2100 if(matchLayoutDirection)
2102 // Swap the alignment type if the line is right to left.
2103 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2105 // Calculate the horizontal line offset.
2106 switch(horizontalAlignment)
2108 case HorizontalAlignment::BEGIN:
2114 lineLength += line.extraLength;
2117 line.alignmentOffset = boxWidth - lineLength;
2121 line.alignmentOffset = 0.f;
2125 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2126 line.alignmentOffset -= line.extraLength;
2131 case HorizontalAlignment::CENTER:
2133 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2137 line.alignmentOffset -= line.extraLength;
2140 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2143 case HorizontalAlignment::END:
2147 line.alignmentOffset = 0.f;
2151 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2152 line.alignmentOffset -= line.extraLength;
2159 lineLength += line.extraLength;
2162 line.alignmentOffset = boxWidth - lineLength;
2169 void Initialize(LineRun& line)
2171 line.glyphRun.glyphIndex = 0u;
2172 line.glyphRun.numberOfGlyphs = 0u;
2173 line.characterRun.characterIndex = 0u;
2174 line.characterRun.numberOfCharacters = 0u;
2176 line.ascender = 0.f;
2177 line.descender = 0.f;
2178 line.extraLength = 0.f;
2179 line.alignmentOffset = 0.f;
2180 line.direction = LTR;
2181 line.ellipsis = false;
2182 line.lineSpacing = mDefaultLineSpacing;
2183 line.isSplitToTwoHalves = false;
2184 line.glyphRunSecondHalf.glyphIndex = 0u;
2185 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2186 line.characterRunForSecondHalfLine.characterIndex = 0u;
2187 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2192 float mDefaultLineSpacing;
2193 float mDefaultLineSize;
2195 IntrusivePtr<Metrics> mMetrics;
2196 float mRelativeLineSize;
2202 mImpl = new Engine::Impl();
2210 void Engine::SetMetrics(MetricsPtr& metrics)
2212 mImpl->mMetrics = metrics;
2215 void Engine::SetLayout(Type layout)
2217 mImpl->mLayout = layout;
2220 Engine::Type Engine::GetLayout() const
2222 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2223 return mImpl->mLayout;
2226 void Engine::SetCursorWidth(int width)
2228 mImpl->mCursorWidth = static_cast<float>(width);
2231 int Engine::GetCursorWidth() const
2233 return static_cast<int>(mImpl->mCursorWidth);
2236 bool Engine::LayoutText(Parameters& layoutParameters,
2238 bool elideTextEnabled,
2239 bool& isAutoScrollEnabled,
2240 DevelText::EllipsisPosition::Type ellipsisPosition)
2242 return mImpl->LayoutText(layoutParameters,
2245 isAutoScrollEnabled,
2249 void Engine::Align(const Size& size,
2250 CharacterIndex startIndex,
2251 Length numberOfCharacters,
2252 Text::HorizontalAlignment::Type horizontalAlignment,
2253 Vector<LineRun>& lines,
2254 float& alignmentOffset,
2255 Dali::LayoutDirection::Type layoutDirection,
2256 bool matchLayoutDirection)
2261 horizontalAlignment,
2265 matchLayoutDirection);
2268 void Engine::SetDefaultLineSpacing(float lineSpacing)
2270 mImpl->mDefaultLineSpacing = lineSpacing;
2273 float Engine::GetDefaultLineSpacing() const
2275 return mImpl->mDefaultLineSpacing;
2278 void Engine::SetDefaultLineSize(float lineSize)
2280 mImpl->mDefaultLineSize = lineSize;
2283 float Engine::GetDefaultLineSize() const
2285 return mImpl->mDefaultLineSize;
2288 void Engine::SetRelativeLineSize(float relativeLineSize)
2290 mImpl->mRelativeLineSize = relativeLineSize;
2293 float Engine::GetRelativeLineSize() const
2295 return mImpl->mRelativeLineSize;
2298 } // namespace Layout
2302 } // namespace Toolkit