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
670 * @param[in] hiddenInputEnabled Whether the hidden input is enabled.
672 void GetLineLayoutForBox(const Parameters& parameters,
673 LayoutBidiParameters& bidiParameters,
674 LineLayout& lineLayout,
676 DevelText::EllipsisPosition::Type ellipsisPosition,
677 bool enforceEllipsisInSingleLine,
678 bool elideTextEnabled,
679 bool hiddenInputEnabled)
681 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
682 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
684 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
685 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
686 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
687 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
688 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
690 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
691 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
693 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
694 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
695 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
696 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
697 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
698 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
700 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
702 // The last glyph to be laid-out.
703 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
705 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
706 // In the case the line starts with a right to left character, if the width is longer than the advance,
707 // the difference needs to be added to the line length.
709 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
710 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
711 lastGlyphOfParagraphPlusOne,
712 charactersPerGlyphBuffer);
714 float targetWidth = parameters.boundingBox.width;
715 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
717 bool isSecondHalf = false;
719 const float modelCharacterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
720 float calculatedAdvance = 0.f;
721 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
722 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
724 // Get the character-spacing runs.
725 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
727 GlyphMetrics glyphMetrics;
728 const float characterSpacing = GetGlyphCharacterSpacing(lineLayout.glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
729 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
730 GetGlyphsMetrics(lineLayout.glyphIndex,
731 numberOfGLyphsInGroup,
737 // Set the direction of the first character of the line.
738 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
740 // Stores temporary line layout which has not been added to the final line layout.
741 LineLayout tmpLineLayout;
743 // Initialize the start point.
745 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
746 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
747 // 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.
748 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
750 tmpLineLayout.relativeLineSize = lineLayout.relativeLineSize;
752 // Calculate the line height if there is no characters.
753 FontId lastFontId = glyphMetrics.fontId;
754 UpdateLineHeight(glyphMetrics, tmpLineLayout);
756 bool oneWordLaidOut = false;
757 bool oneHyphenLaidOut = false;
758 GlyphIndex hyphenIndex = 0;
759 GlyphInfo hyphenGlyph;
761 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
762 glyphIndex < lastGlyphOfParagraphPlusOne;)
764 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
766 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
767 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
768 lastGlyphOfParagraphPlusOne,
769 charactersPerGlyphBuffer);
771 GlyphMetrics glyphMetrics;
772 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
773 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
774 GetGlyphsMetrics(glyphIndex,
775 numberOfGLyphsInGroup,
781 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
783 // Check if the font of the current glyph is the same of the previous one.
784 // If it's different the ascender and descender need to be updated.
785 if(lastFontId != glyphMetrics.fontId)
787 UpdateLineHeight(glyphMetrics, tmpLineLayout);
788 lastFontId = glyphMetrics.fontId;
791 // Get the character indices for the current glyph. The last character index is needed
792 // because there are glyphs formed by more than one character but their break info is
793 // given only for the last character.
794 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
795 const bool hasCharacters = charactersPerGlyph > 0u;
796 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
797 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
799 // Get the line break info for the current character.
800 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
804 // Increase the number of characters.
805 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
807 // Increase the number of glyphs.
808 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
812 // Increase the number of characters.
813 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
815 // Increase the number of glyphs.
816 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
819 // Check whether is a white space.
820 const Character character = *(textBuffer + characterFirstIndex);
821 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
823 // Calculate the length of the line.
825 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
826 const float previousTmpPenX = tmpLineLayout.penX;
827 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
828 const float previousTmpLength = tmpLineLayout.length;
829 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
831 // The calculated text size is used in atlas renderer.
832 // When the text is all white space, partial render issue occurs because the width is 0.
833 // To avoid issue, do not remove the white space size in hidden input mode.
834 if(isWhiteSpace && !hiddenInputEnabled)
836 // Add the length to the length of white spaces at the end of the line.
837 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
838 // The advance is used as the width is always zero for the white spaces.
842 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
843 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
845 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
847 // Clear the white space length at the end of the line.
848 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
851 if(isSplitToTwoHalves && (!isSecondHalf) &&
852 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
854 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
855 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
857 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
858 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
860 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
861 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
863 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
865 // Check if the accumulated length fits in the width of the box.
866 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
867 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
868 completelyFill && !isMultiline &&
869 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
871 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
873 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
875 GlyphMetrics glyphMetrics;
876 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndexToRemove, characterSpacingGlyphRuns, modelCharacterSpacing);
877 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
878 GetGlyphsMetrics(glyphIndexToRemove,
879 numberOfGLyphsInGroup,
885 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
886 lastGlyphOfParagraphPlusOne,
887 charactersPerGlyphBuffer);
889 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
890 const bool hasCharacters = charactersPerGlyph > 0u;
891 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
892 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
894 // Check whether is a white space.
895 const Character character = *(textBuffer + characterFirstIndex);
896 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
900 // Decrease the number of characters for SecondHalf.
901 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
903 // Decrease the number of glyphs for SecondHalf.
904 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
908 // Decrease the number of characters.
909 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
911 // Decrease the number of glyphs.
912 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
915 if(isRemovedGlyphWhiteSpace && !hiddenInputEnabled)
917 tmpLineLayout.penX -= glyphMetrics.advance;
918 tmpLineLayout.length -= glyphMetrics.advance;
922 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
923 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
928 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
929 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
930 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
934 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
935 tmpLineLayout.characterIndex = characterLastIndex + 1u;
936 glyphIndexToRemove = tmpLineLayout.glyphIndex;
940 else if((completelyFill || isMultiline) &&
941 (tmpLineLayout.length > targetWidth))
943 // Current word does not fit in the box's width.
944 if(((oneHyphenLaidOut && isHyphenMode) ||
945 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
948 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
949 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
952 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
954 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
956 // The word doesn't fit in the control's width. It needs to be split by character.
957 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
961 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
962 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
966 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
967 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
970 tmpLineLayout.penX = previousTmpPenX;
971 tmpLineLayout.previousAdvance = previousTmpAdvance;
972 tmpLineLayout.length = previousTmpLength;
973 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
976 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
978 // Add part of the word to the line layout and shift the first glyph.
979 MergeLineLayout(lineLayout, tmpLineLayout, true);
981 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
982 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
984 // Add part of the word to the line layout.
985 MergeLineLayout(lineLayout, tmpLineLayout, false);
990 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
993 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
995 // Reorder the RTL line.
996 if(bidiParameters.isBidirectional)
998 ReorderBiDiLayout(parameters,
1003 enforceEllipsisInSingleLine);
1009 if((isMultiline || isLastGlyph) &&
1010 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
1012 LineLayout currentLineLayout = lineLayout;
1013 oneHyphenLaidOut = false;
1015 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1017 // Must break the line. Update the line layout, shift the first glyph and return.
1018 MergeLineLayout(lineLayout, tmpLineLayout, true);
1022 // Must break the line. Update the line layout and return.
1023 MergeLineLayout(lineLayout, tmpLineLayout, false);
1026 // Reorder the RTL line.
1027 if(bidiParameters.isBidirectional)
1029 ReorderBiDiLayout(parameters,
1034 enforceEllipsisInSingleLine);
1037 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
1038 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1044 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1046 oneHyphenLaidOut = false;
1047 oneWordLaidOut = isWordLaidOut;
1048 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
1050 // Current glyph is the last one of the current word.
1051 // Add the temporal layout to the current one.
1052 MergeLineLayout(lineLayout, tmpLineLayout, false);
1054 tmpLineLayout.Clear();
1058 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1059 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1061 hyphenGlyph = GlyphInfo();
1062 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1064 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1065 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1067 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1069 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1071 hyphenIndex = glyphIndex;
1072 oneHyphenLaidOut = true;
1074 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1076 // Current glyph is the last one of the current word hyphen.
1077 // Add the temporal layout to the current one.
1078 MergeLineLayout(lineLayout, tmpLineLayout, false);
1080 tmpLineLayout.Clear();
1084 glyphIndex += numberOfGLyphsInGroup;
1087 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1090 void SetGlyphPositions(const Parameters& layoutParameters,
1091 Vector2* glyphPositionsBuffer,
1092 const LineLayout& layout)
1094 // Traverse the glyphs and set the positions.
1096 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1097 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1098 const Length numberOfGlyphs = layout.numberOfGlyphs;
1099 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1101 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1102 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1104 // Check if the x bearing of the first character is negative.
1105 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1106 // so the penX position needs to be moved to the right.
1107 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1108 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1110 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1111 layoutParameters.textModel->mLogicalModel,
1112 interGlyphExtraAdvance,
1114 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1115 startIndexForGlyphPositions,
1116 glyphPositionsBuffer,
1119 if(layout.isSplitToTwoHalves)
1121 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1122 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1123 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1125 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1126 layoutParameters.textModel->mLogicalModel,
1127 interGlyphExtraAdvance,
1128 numberOfGlyphsInSecondHalfLine,
1129 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1130 startIndexForGlyphPositionsnSecondHalf,
1131 glyphPositionsBuffer,
1136 void SetGlyphPositions(const Parameters& layoutParameters,
1137 Vector2* glyphPositionsBuffer,
1138 LayoutBidiParameters& layoutBidiParameters,
1139 const LineLayout& layout)
1141 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1142 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1143 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1145 CharacterIndex characterLogicalIndex = 0u;
1146 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1147 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1151 if(layout.isSplitToTwoHalves)
1153 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1154 layoutParameters.textModel->mLogicalModel,
1155 layoutBidiParameters.bidiLineIndex,
1156 layoutParameters.startGlyphIndex,
1157 glyphPositionsBuffer,
1158 characterVisualIndex,
1159 characterLogicalIndex,
1163 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1165 extendedToSecondHalf = true;
1166 characterLogicalIndex = 0u;
1167 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1169 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1170 layoutParameters.textModel->mLogicalModel,
1171 layoutBidiParameters.bidiLineIndex,
1172 layoutParameters.startGlyphIndex,
1173 glyphPositionsBuffer,
1174 characterVisualIndex,
1175 characterLogicalIndex,
1179 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1180 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1182 penX += -glyph.xBearing;
1184 // Traverses the characters of the right to left paragraph.
1185 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1187 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1188 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1189 layoutParameters.startGlyphIndex,
1190 layoutParameters.interGlyphExtraAdvance,
1191 bidiLine.characterRunForSecondHalfLine,
1192 bidiLine.visualToLogicalMapSecondHalf,
1193 glyphPositionsBuffer,
1194 characterLogicalIndex,
1198 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1200 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1201 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1202 layoutParameters.startGlyphIndex,
1203 layoutParameters.interGlyphExtraAdvance,
1204 bidiLine.characterRun,
1205 bidiLine.visualToLogicalMap,
1206 glyphPositionsBuffer,
1207 characterLogicalIndex,
1212 * @brief Resizes the line buffer.
1214 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1215 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1216 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1217 * @param[in] updateCurrentBuffer Whether the layout is updated.
1219 * @return Pointer to either lines or newLines.
1221 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1222 Vector<LineRun>& newLines,
1223 Length& linesCapacity,
1224 bool updateCurrentBuffer)
1226 LineRun* linesBuffer = nullptr;
1227 // Reserve more space for the next lines.
1228 linesCapacity *= 2u;
1229 if(updateCurrentBuffer)
1231 newLines.Resize(linesCapacity);
1232 linesBuffer = newLines.Begin();
1236 lines.Resize(linesCapacity);
1237 linesBuffer = lines.Begin();
1244 * Ellipsis a line if it exceeds the width's of the bounding box.
1246 * @param[in] layoutParameters The parameters needed to layout the text.
1247 * @param[in] layout The line layout.
1248 * @param[in,out] layoutSize The text's layout size.
1249 * @param[in,out] linesBuffer Pointer to the line's buffer.
1250 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1251 * @param[in,out] numberOfLines The number of laid-out lines.
1252 * @param[in] penY The vertical layout position.
1253 * @param[in] currentParagraphDirection The current paragraph's direction.
1254 * @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
1255 * @param[in] isHiddenInputEnabled Whether the hidden input is enabled.
1256 * @param[in] ellipsisPosition Where is the location the text elide
1258 * return Whether the line is ellipsized.
1260 bool EllipsisLine(const Parameters& layoutParameters,
1261 LayoutBidiParameters& layoutBidiParameters,
1262 const LineLayout& layout,
1264 LineRun* linesBuffer,
1265 Vector2* glyphPositionsBuffer,
1266 Length& numberOfLines,
1268 bool& isAutoScrollEnabled,
1269 bool isAutoScrollMaxTextureExceeded,
1270 bool isHiddenInputEnabled,
1271 DevelText::EllipsisPosition::Type ellipsisPosition,
1272 bool enforceEllipsisInSingleLine)
1274 const bool ellipsis = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? isAutoScrollMaxTextureExceeded : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
1275 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1276 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1278 if(penY - layout.descender > layoutParameters.boundingBox.height)
1280 // Even if auto scroll is enabled and text is bigger than max texture size,
1281 // if the the height is small, auto scroll should not work.
1282 isAutoScrollEnabled = false;
1285 // Do not layout more lines if ellipsis is enabled.
1286 // The last line needs to be completely filled with characters.
1287 // Part of a word may be used.
1289 LineRun* lineRun = nullptr;
1290 LineLayout ellipsisLayout;
1292 ellipsisLayout.relativeLineSize = layout.relativeLineSize;
1294 if(0u != numberOfLines)
1296 // Get the last line and layout it again with the 'completelyFill' flag to true.
1297 lineRun = linesBuffer + (numberOfLines - 1u);
1298 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1300 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1304 // At least there is space reserved for one line.
1305 lineRun = linesBuffer;
1307 lineRun->glyphRun.glyphIndex = 0u;
1308 ellipsisLayout.glyphIndex = 0u;
1309 lineRun->isSplitToTwoHalves = false;
1314 GetLineLayoutForBox(layoutParameters,
1315 layoutBidiParameters,
1319 enforceEllipsisInSingleLine,
1321 isHiddenInputEnabled);
1323 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1325 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1328 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1329 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1330 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1331 lineRun->width = ellipsisLayout.length;
1332 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1333 lineRun->ascender = ellipsisLayout.ascender;
1334 lineRun->descender = ellipsisLayout.descender;
1335 lineRun->ellipsis = true;
1337 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1338 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1339 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1340 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1341 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1343 layoutSize.width = layoutParameters.boundingBox.width;
1344 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1346 layoutSize.height += GetLineHeight(*lineRun, true);
1350 //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1351 //usually we don't includ it in normal cases using GetLineHeight()
1352 if(lineRun->lineSpacing < 0)
1354 layoutSize.height -= lineRun->lineSpacing;
1358 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1360 if(layoutBidiParameters.isBidirectional)
1362 layoutBidiParameters.bidiLineIndex = 0u;
1363 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1364 endIt = bidirectionalLinesInfo.End();
1366 ++it, ++layoutBidiParameters.bidiLineIndex)
1368 const BidirectionalLineInfoRun& run = *it;
1369 //To handle case when the laid characters exist in next line.
1370 //More than one BidirectionalLineInfoRun could start with same character.
1371 //When need to check also numberOfCharacters in line.
1372 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1373 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1374 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1375 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1376 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1378 // Found where to insert the bidi line info.
1384 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1386 if((nullptr != bidirectionalLineInfo) &&
1387 !bidirectionalLineInfo->isIdentity &&
1388 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1390 lineRun->direction = RTL;
1391 SetGlyphPositions(layoutParameters,
1392 glyphPositionsBuffer,
1393 layoutBidiParameters,
1398 lineRun->direction = LTR;
1399 SetGlyphPositions(layoutParameters,
1400 glyphPositionsBuffer,
1409 * @brief Updates the text layout with a new laid-out line.
1411 * @param[in] layoutParameters The parameters needed to layout the text.
1412 * @param[in] layout The line layout.
1413 * @param[in,out] layoutSize The text's layout size.
1414 * @param[in,out] linesBuffer Pointer to the line's buffer.
1415 * @param[in] index Index to the vector of glyphs.
1416 * @param[in,out] numberOfLines The number of laid-out lines.
1417 * @param[in] isLastLine Whether the laid-out line is the last one.
1419 void UpdateTextLayout(const Parameters& layoutParameters,
1420 const LineLayout& layout,
1422 LineRun* linesBuffer,
1424 Length& numberOfLines,
1427 LineRun& lineRun = *(linesBuffer + numberOfLines);
1430 lineRun.glyphRun.glyphIndex = index;
1431 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1432 lineRun.characterRun.characterIndex = layout.characterIndex;
1433 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1434 lineRun.width = layout.length;
1435 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1437 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1438 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1439 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1440 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1441 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1443 // Rounds upward to avoid a non integer size.
1444 lineRun.width = std::ceil(lineRun.width);
1446 lineRun.ascender = layout.ascender;
1447 lineRun.descender = layout.descender;
1448 lineRun.direction = layout.direction;
1449 lineRun.ellipsis = false;
1451 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
1453 // Update the actual size.
1454 if(lineRun.width > layoutSize.width)
1456 layoutSize.width = lineRun.width;
1459 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1463 * @brief Updates the text layout with the last laid-out line.
1465 * @param[in] layoutParameters The parameters needed to layout the text.
1466 * @param[in] characterIndex The character index of the line.
1467 * @param[in] glyphIndex The glyph index of the line.
1468 * @param[in,out] layoutSize The text's layout size.
1469 * @param[in,out] linesBuffer Pointer to the line's buffer.
1470 * @param[in,out] numberOfLines The number of laid-out lines.
1472 void UpdateTextLayout(const Parameters& layoutParameters,
1473 CharacterIndex characterIndex,
1474 GlyphIndex glyphIndex,
1476 LineRun* linesBuffer,
1477 Length& numberOfLines)
1479 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1481 // Need to add a new line with no characters but with height to increase the layoutSize.height
1482 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1484 Text::FontMetrics fontMetrics;
1485 if(0u != glyphInfo.fontId)
1487 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1490 LineRun& lineRun = *(linesBuffer + numberOfLines);
1493 lineRun.glyphRun.glyphIndex = glyphIndex;
1494 lineRun.glyphRun.numberOfGlyphs = 0u;
1495 lineRun.characterRun.characterIndex = characterIndex;
1496 lineRun.characterRun.numberOfCharacters = 0u;
1497 lineRun.width = 0.f;
1498 lineRun.ascender = fontMetrics.ascender;
1499 lineRun.descender = fontMetrics.descender;
1500 lineRun.extraLength = 0.f;
1501 lineRun.alignmentOffset = 0.f;
1502 lineRun.direction = LTR;
1503 lineRun.ellipsis = false;
1505 BoundedParagraphRun currentParagraphRun;
1506 LineLayout tempLineLayout;
1507 (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
1509 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
1511 layoutSize.height += GetLineHeight(lineRun, true);
1515 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1517 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1518 * @param[in,out] layoutSize The text's layout size.
1520 void UpdateLayoutSize(const Vector<LineRun>& lines,
1523 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1524 endIt = lines.End();
1528 const LineRun& line = *it;
1529 bool isLastLine = (it + 1 == endIt);
1531 if(line.width > layoutSize.width)
1533 layoutSize.width = line.width;
1536 layoutSize.height += GetLineHeight(line, isLastLine);
1541 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1543 * @param[in] layoutParameters The parameters needed to layout the text.
1544 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1545 * @param[in] characterOffset The offset to be added to the runs of characters.
1546 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1548 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1549 Vector<LineRun>& lines,
1550 Length characterOffset,
1553 // Update the glyph and character runs.
1554 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1555 endIt = lines.End();
1559 LineRun& line = *it;
1561 line.glyphRun.glyphIndex = glyphOffset;
1562 line.characterRun.characterIndex = characterOffset;
1564 glyphOffset += line.glyphRun.numberOfGlyphs;
1565 characterOffset += line.characterRun.numberOfCharacters;
1570 * @brief Sets the relative line size for the LineLayout
1572 * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
1573 * @param[in,out] lineLayout The line layout to be updated.
1575 void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
1577 lineLayout.relativeLineSize = mRelativeLineSize;
1579 if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
1581 lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
1586 * @brief Get the bounded paragraph for the characterIndex if exists.
1588 * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
1589 * @param[in] characterIndex The character index to get bounded paragraph for.
1590 * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
1592 * @return returns true if a bounded paragraph was found.
1594 bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
1596 for(Vector<BoundedParagraphRun>::Iterator it = boundedParagraphRuns.Begin(),
1597 endIt = boundedParagraphRuns.End();
1601 BoundedParagraphRun& tempParagraphRun = *it;
1603 if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
1604 characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
1606 currentParagraphRun = tempParagraphRun;
1614 bool LayoutText(Parameters& layoutParameters,
1616 bool elideTextEnabled,
1617 bool& isAutoScrollEnabled,
1618 bool isAutoScrollMaxTextureExceeded,
1619 bool isHiddenInputEnabled,
1620 DevelText::EllipsisPosition::Type ellipsisPosition)
1622 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1623 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1625 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1626 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1628 //Reset indices of ElidedGlyphs
1629 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1630 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1631 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1632 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1634 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1635 const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
1637 if(0u == layoutParameters.numberOfGlyphs)
1639 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1640 if(layoutParameters.isLastNewParagraph)
1642 Length numberOfLines = lines.Count();
1643 if(0u != numberOfLines)
1645 const LineRun& lastLine = *(lines.End() - 1u);
1647 if(0u != lastLine.characterRun.numberOfCharacters)
1649 // Need to add a new line with no characters but with height to increase the layoutSize.height
1651 Initialize(newLine);
1652 lines.PushBack(newLine);
1654 UpdateTextLayout(layoutParameters,
1655 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1656 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1664 // Calculates the layout size.
1665 UpdateLayoutSize(lines,
1668 // Rounds upward to avoid a non integer size.
1669 layoutSize.height = std::ceil(layoutSize.height);
1671 // Nothing else do if there are no glyphs to layout.
1675 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1676 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1677 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1679 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1680 // This extra line needs to be removed.
1681 if(0u != lines.Count())
1683 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1685 if((0u == lastLine->characterRun.numberOfCharacters) &&
1686 (lastGlyphPlusOne == totalNumberOfGlyphs))
1688 lines.Remove(lastLine);
1692 // Retrieve BiDi info.
1693 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1695 const CharacterIndex* const glyphsToCharactersBuffer = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
1696 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1697 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1699 // Set the layout bidirectional paramters.
1700 LayoutBidiParameters layoutBidiParameters;
1702 // Whether the layout is being updated or set from scratch.
1703 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1705 Vector2* glyphPositionsBuffer = nullptr;
1706 Vector<Vector2> newGlyphPositions;
1708 LineRun* linesBuffer = nullptr;
1709 Vector<LineRun> newLines;
1711 // Estimate the number of lines.
1712 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1713 Length numberOfLines = 0u;
1715 if(updateCurrentBuffer)
1717 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1718 glyphPositionsBuffer = newGlyphPositions.Begin();
1720 newLines.Resize(linesCapacity);
1721 linesBuffer = newLines.Begin();
1725 glyphPositionsBuffer = glyphPositions.Begin();
1727 lines.Resize(linesCapacity);
1728 linesBuffer = lines.Begin();
1731 float penY = CalculateLineOffset(lines,
1732 layoutParameters.startLineIndex);
1733 bool anyLineIsEliped = false;
1734 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1736 layoutBidiParameters.Clear();
1738 if(hasBidiParagraphs)
1740 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1742 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1743 endIt = bidirectionalParagraphsInfo.End();
1745 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1747 const BidirectionalParagraphInfoRun& run = *it;
1749 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1751 if(lastCharacterIndex <= startCharacterIndex)
1753 // Do not process, the paragraph has already been processed.
1757 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1759 layoutBidiParameters.paragraphDirection = run.direction;
1760 layoutBidiParameters.isBidirectional = true;
1763 // Has already been found.
1767 if(layoutBidiParameters.isBidirectional)
1769 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1770 endIt = bidirectionalLinesInfo.End();
1772 ++it, ++layoutBidiParameters.bidiLineIndex)
1774 const BidirectionalLineInfoRun& run = *it;
1776 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1778 if(lastCharacterIndex <= startCharacterIndex)
1784 if(startCharacterIndex < lastCharacterIndex)
1786 // Found where to insert the bidi line info.
1793 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1795 // Get the layout for the line.
1797 layout.direction = layoutBidiParameters.paragraphDirection;
1798 layout.glyphIndex = index;
1800 BoundedParagraphRun currentParagraphRun;
1801 (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(¤tParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
1803 GetLineLayoutForBox(layoutParameters,
1804 layoutBidiParameters,
1810 isHiddenInputEnabled);
1812 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1813 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1814 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1815 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1816 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1818 CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
1820 //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)
1821 if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex + layout.numberOfCharacters)
1823 layout.relativeLineSize = mRelativeLineSize;
1826 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1828 // The width is too small and no characters are laid-out.
1829 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1831 lines.Resize(numberOfLines);
1833 // Rounds upward to avoid a non integer size.
1834 layoutSize.height = std::ceil(layoutSize.height);
1839 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1841 penY += layout.ascender;
1843 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1845 bool ellipsis = false;
1846 if(elideTextEnabled)
1848 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1850 // Does the ellipsis of the last line.
1851 ellipsis = EllipsisLine(layoutParameters,
1852 layoutBidiParameters,
1856 glyphPositionsBuffer,
1859 isAutoScrollEnabled,
1860 isAutoScrollMaxTextureExceeded,
1861 isHiddenInputEnabled,
1866 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1868 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1869 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1871 ellipsis = EllipsisLine(layoutParameters,
1872 layoutBidiParameters,
1876 glyphPositionsBuffer,
1879 isAutoScrollEnabled,
1880 isAutoScrollMaxTextureExceeded,
1881 isHiddenInputEnabled,
1886 //clear hyphen from ellipsis line
1887 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1888 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1890 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1892 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1893 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1897 // No more lines to layout.
1902 //In START location of ellipsis whether to shift lines or not.
1903 anyLineIsEliped |= ellipsis;
1905 // Whether the last line has been laid-out.
1906 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1908 if(numberOfLines == linesCapacity)
1910 // Reserve more space for the next lines.
1911 linesBuffer = ResizeLinesBuffer(lines,
1914 updateCurrentBuffer);
1917 // Updates the current text's layout with the line's layout.
1918 UpdateTextLayout(layoutParameters,
1926 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1928 if((nextIndex == totalNumberOfGlyphs) &&
1929 layoutParameters.isLastNewParagraph &&
1930 (mLayout == MULTI_LINE_BOX))
1932 // The last character of the text is a new paragraph character.
1933 // An extra line with no characters is added to increase the text's height
1934 // in order to place the cursor.
1936 if(numberOfLines == linesCapacity)
1938 // Reserve more space for the next lines.
1939 linesBuffer = ResizeLinesBuffer(lines,
1942 updateCurrentBuffer);
1945 UpdateTextLayout(layoutParameters,
1946 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1947 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1951 } // whether to add a last line.
1953 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1955 if((nullptr != bidirectionalLineInfo) &&
1956 !bidirectionalLineInfo->isIdentity &&
1957 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1959 SetGlyphPositions(layoutParameters,
1960 glyphPositionsBuffer,
1961 layoutBidiParameters,
1966 // Sets the positions of the glyphs.
1967 SetGlyphPositions(layoutParameters,
1968 glyphPositionsBuffer,
1972 // Updates the vertical pen's position.
1973 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
1975 // Increase the glyph index.
1978 } // end for() traversing glyphs.
1980 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1981 if(anyLineIsEliped && numberOfLines > 1u)
1983 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1985 Length lineIndex = 0;
1986 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1988 LineRun& delLine = linesBuffer[lineIndex];
1989 delLine.ellipsis = true;
1991 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1992 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1994 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1995 linesBuffer[lineIndex].ellipsis = false;
1999 linesBuffer[0u].ellipsis = true;
2001 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
2003 Length middleLineIndex = (numberOfLines) / 2u;
2004 Length ellipsisLineIndex = 0u;
2005 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
2007 LineRun& delLine = linesBuffer[middleLineIndex];
2008 delLine.ellipsis = true;
2010 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
2011 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
2013 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
2014 linesBuffer[lineIndex].ellipsis = false;
2017 ellipsisLineIndex = middleLineIndex - 1u;
2018 middleLineIndex = (numberOfLines) / 2u;
2021 linesBuffer[ellipsisLineIndex].ellipsis = true;
2025 if(updateCurrentBuffer)
2027 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
2028 newGlyphPositions.Begin(),
2029 newGlyphPositions.End());
2030 glyphPositions.Resize(totalNumberOfGlyphs);
2032 newLines.Resize(numberOfLines);
2034 // Current text's layout size adds only the newly laid-out lines.
2035 // Updates the layout size with the previously laid-out lines.
2036 UpdateLayoutSize(lines,
2039 if(0u != newLines.Count())
2041 const LineRun& lastLine = *(newLines.End() - 1u);
2043 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
2044 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
2046 // Update the indices of the runs before the new laid-out lines are inserted.
2047 UpdateLineIndexOffsets(layoutParameters,
2052 // Insert the lines.
2053 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
2060 lines.Resize(numberOfLines);
2063 // Rounds upward to avoid a non integer size.
2064 layoutSize.height = std::ceil(layoutSize.height);
2066 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
2071 void Align(const Size& size,
2072 CharacterIndex startIndex,
2073 Length numberOfCharacters,
2074 Text::HorizontalAlignment::Type horizontalAlignment,
2075 Vector<LineRun>& lines,
2076 float& alignmentOffset,
2077 Dali::LayoutDirection::Type layoutDirection,
2078 bool matchLayoutDirection)
2080 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
2082 alignmentOffset = MAX_FLOAT;
2083 // Traverse all lines and align the glyphs.
2084 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
2088 LineRun& line = *it;
2090 if(line.characterRun.characterIndex < startIndex)
2092 // Do not align lines which have already been aligned.
2096 if(line.characterRun.characterIndex > lastCharacterPlusOne)
2098 // Do not align lines beyond the last laid-out character.
2102 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
2104 // Do not align lines beyond the last laid-out character unless the line is last and empty.
2108 // Calculate the line's alignment offset accordingly with the align option,
2109 // the box width, line length, and the paragraph's direction.
2110 CalculateHorizontalAlignment(size.width,
2111 horizontalAlignment,
2114 matchLayoutDirection);
2116 // Updates the alignment offset.
2117 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2121 void CalculateHorizontalAlignment(float boxWidth,
2122 HorizontalAlignment::Type horizontalAlignment,
2124 Dali::LayoutDirection::Type layoutDirection,
2125 bool matchLayoutDirection)
2127 line.alignmentOffset = 0.f;
2128 const bool isLineRTL = RTL == line.direction;
2130 // Whether to swap the alignment.
2131 // 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.
2132 bool isLayoutRTL = isLineRTL;
2133 float lineLength = line.width;
2135 // match align for system language direction
2136 if(matchLayoutDirection)
2138 // Swap the alignment type if the line is right to left.
2139 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2141 // Calculate the horizontal line offset.
2142 switch(horizontalAlignment)
2144 case HorizontalAlignment::BEGIN:
2150 lineLength += line.extraLength;
2153 line.alignmentOffset = boxWidth - lineLength;
2157 line.alignmentOffset = 0.f;
2161 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2162 line.alignmentOffset -= line.extraLength;
2167 case HorizontalAlignment::CENTER:
2169 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2173 line.alignmentOffset -= line.extraLength;
2176 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2179 case HorizontalAlignment::END:
2183 line.alignmentOffset = 0.f;
2187 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2188 line.alignmentOffset -= line.extraLength;
2195 lineLength += line.extraLength;
2198 line.alignmentOffset = boxWidth - lineLength;
2205 void Initialize(LineRun& line)
2207 line.glyphRun.glyphIndex = 0u;
2208 line.glyphRun.numberOfGlyphs = 0u;
2209 line.characterRun.characterIndex = 0u;
2210 line.characterRun.numberOfCharacters = 0u;
2212 line.ascender = 0.f;
2213 line.descender = 0.f;
2214 line.extraLength = 0.f;
2215 line.alignmentOffset = 0.f;
2216 line.direction = LTR;
2217 line.ellipsis = false;
2218 line.lineSpacing = mDefaultLineSpacing;
2219 line.isSplitToTwoHalves = false;
2220 line.glyphRunSecondHalf.glyphIndex = 0u;
2221 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2222 line.characterRunForSecondHalfLine.characterIndex = 0u;
2223 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2228 float mDefaultLineSpacing;
2229 float mDefaultLineSize;
2231 IntrusivePtr<Metrics> mMetrics;
2232 float mRelativeLineSize;
2238 mImpl = new Engine::Impl();
2246 void Engine::SetMetrics(MetricsPtr& metrics)
2248 mImpl->mMetrics = metrics;
2251 void Engine::SetLayout(Type layout)
2253 mImpl->mLayout = layout;
2256 Engine::Type Engine::GetLayout() const
2258 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2259 return mImpl->mLayout;
2262 void Engine::SetCursorWidth(int width)
2264 mImpl->mCursorWidth = static_cast<float>(width);
2267 int Engine::GetCursorWidth() const
2269 return static_cast<int>(mImpl->mCursorWidth);
2272 bool Engine::LayoutText(Parameters& layoutParameters,
2274 bool elideTextEnabled,
2275 bool& isAutoScrollEnabled,
2276 bool isAutoScrollMaxTextureExceeded,
2277 bool isHiddenInputEnabled,
2278 DevelText::EllipsisPosition::Type ellipsisPosition)
2280 return mImpl->LayoutText(layoutParameters,
2283 isAutoScrollEnabled,
2284 isAutoScrollMaxTextureExceeded,
2285 isHiddenInputEnabled,
2289 void Engine::Align(const Size& size,
2290 CharacterIndex startIndex,
2291 Length numberOfCharacters,
2292 Text::HorizontalAlignment::Type horizontalAlignment,
2293 Vector<LineRun>& lines,
2294 float& alignmentOffset,
2295 Dali::LayoutDirection::Type layoutDirection,
2296 bool matchLayoutDirection)
2301 horizontalAlignment,
2305 matchLayoutDirection);
2308 void Engine::SetDefaultLineSpacing(float lineSpacing)
2310 mImpl->mDefaultLineSpacing = lineSpacing;
2313 float Engine::GetDefaultLineSpacing() const
2315 return mImpl->mDefaultLineSpacing;
2318 void Engine::SetDefaultLineSize(float lineSize)
2320 mImpl->mDefaultLineSize = lineSize;
2323 float Engine::GetDefaultLineSize() const
2325 return mImpl->mDefaultLineSize;
2328 void Engine::SetRelativeLineSize(float relativeLineSize)
2330 mImpl->mRelativeLineSize = relativeLineSize;
2333 float Engine::GetRelativeLineSize() const
2335 return mImpl->mRelativeLineSize;
2338 } // namespace Layout
2342 } // namespace Toolkit